This is for the latest 2019 PLANEA test!

Easy ggplot theme.

Let’s define some function to interact easily with the database.

#' @title plan_connect
#'
#' @description This function takes the Postgres environment variables
#' to automatically connect to the planeadb database.
#' The connection must be saved into a variable in order to
#' use future functions. I'm cheating and not reading from .env.
#'
#' @examples con <- prev_connect()
#'
#' @param No parameter is needed.
#' @export
plan_connect <- function(){
  DBI::dbConnect(RPostgreSQL::PostgreSQL(),
  host     =  "localhost",
  user     =  "planea",
  password =  "planea",
  port     =  5432,
  dbname   =  "planea")
}

#' @title load_query
#'
#' @description Allows to run specific queries with the dbrsocial syntax.
#'
#' @param connection DBI connection. A connection to a database
#' @param schema variable. A valid schema from a database on the
#' @param the_table.  An existing table in the given schema.
#' @param colums string. The columns in the database we want to retrieve
#' information
#' @param options string. Part of the SQL query with containing WHERE, ORDER,
#' LIMIT and so statements
#'
#' @examples geom_muni <-
#' load_query(con,raw,sifode,columns="entidadfederativa",options="WHERE
#' mes='Abril'")
#' @export
load_query <- function(connection,schema,the_table,columns="*",options=""){
    the_query <- "SELECT %s FROM %s.%s"
    complete <- paste0(the_query," ",options)
    schema    <- deparse(substitute(schema))
    the_table <- deparse(substitute(the_table))
    initial <- RPostgreSQL::dbSendQuery(connection,
                             sprintf(complete,columns,schema,the_table))
    return(initial)
}

#' @title load_table
#'
#' @description This function loads a connection to
#' a given table from a particular schema in a database
#' connection.
#'
#' @param connection DBI connection. A connection to a database
#' must be open and given.
#' @param schema variable. A valid schema from a database on the
#' connected database.
#' @param the_table. An existing table in the given schema.
#'
#' @examples the_dic<-load_table(con,raw,sifode_dic)
#' @export
load_table <- function(connection,schema,the_table){
    the_query <- "SELECT * FROM %s.%s"
    schema    <- deparse(substitute(schema))
    the_table <- deparse(substitute(the_table))
    initial <- RPostgreSQL::dbSendQuery(connection,
                             sprintf(the_query,schema,the_table))
}

#' @title large_table
#'
#' @description This function loads a connection to a large table without
#' loading it to memory.
#'
#' @param connection DBI connection. A connection to a database
#' must be open and given.
#' @param schema variable. A valid schema from a database on the
#' connected database.
#' @param the_table. An existing table in the given schema.
#'
#' @examples cuis_table <- large_table(con,raw,cuis_39_9)
#' @export
large_table <- function(connection,schema,the_table){
    schema    <- deparse(substitute(schema))
    the_table <- deparse(substitute(the_table))
    retrieved <- dplyr::tbl(connection,dbplyr::in_schema(schema,the_table))
    return(retrieved)
}

#' @title clear_results
#'
#' @description This function clears the results from a previous executed
#' query.
#'
#' @param connection DBI connection. A connection to a database must be open and given.
#'
#' @examples clear_results(con)
#' @export
clear_results <- function(connection){
    DBI::dbClearResult(DBI::dbListResults(connection)[[1]])
}

#' @title join_tables
#'
#' @description Returns a match between two tbl by defined key
#' @param left_table a tbl-like object
#' @param right_table a tbl-like object
#' @param left_key the column name from left_table to compare
#' @param right_key the column name from right_table to compare
#'
#' @examples join_tables(cuis_sample,llave_hogar_h,domicilios_sample_query,llave_hogar_h)
#' @export
join_tables <- function(left_table, left_key, right_table, right_key){
    left_key <- substitute(left_key)
    right_key <- deparse(substitute(right_key))
    in_tables <- left_table %>%
        dplyr::filter(left_key %in% right_table[[right_key]])
    return(in_tables)
}

#' @title retrieve_result
#'
#' @description Return the fetch results of a query
#' @param query An exec unfetched query
#'
#' @examples sample_table(load_table(prev_connect(),raw,sifode))
#' @export
retrieve_result <- function(query,n=-1,number=Inf){
    if (class(query)[1] == "tbl_dbi"){
        the_table <- dplyr::collect(query,n=number)
        return(the_table)
    }
    else{
    the_table <- DBI::dbFetch(query,n)
    return(the_table)
    }
}

#' @title load_geom
#'
#' @description Gives a "ready to go" data frame for geometry plotting
#'
#' @param connection DBI connection. A connection to a database
#' @param schema variable. A valid schema from a database on the
#' @param the_table.  An existing table in the given schema.
#' @param colums string. The columns in the database we want to retrieve.
#' Defaults cve_ent, cve_mun, cve_muni.
#' @param geom_col . The name of the column in the database that contains a geometry
#' @param col_shape. The name of the column that we want to use to join
#' information
#' @param options string. Part of the SQL query with containing WHERE, ORDER,
#' LIMIT and so statements
#'
#' @examples geom_muni <- load_geom(con1,raw,geom_municipios,geom_col=geom,col_shape=cve_muni,options=options)
#' @export
load_geom <- function(connection,schema,the_table,columns="\"CVEGEO\", \"CVE_ENT\", \"CVE_MUN\"", geom_col, col_shape, options=""){
    geom_col <- deparse(substitute(geom_col))
    schema    <- deparse(substitute(schema))
    the_table <- deparse(substitute(the_table))
    col_shape <- deparse(substitute(col_shape))
    geom_col2 <- paste0("\"",geom_col,"\"")

    the_query <- "SELECT %s FROM %s.%s"
    geom_col_as <- sprintf(", %s as geom",geom_col2)
    columns <- paste0(columns,geom_col_as)
    complete <- paste0(the_query," ",options)

    initial <- RPostgreSQL::dbSendQuery(connection,
                             sprintf(complete,columns,schema,the_table)) %>%
    retrieve_result()

    mun_shp = rangeMapper::WKT2SpatialPolygonsDataFrame(initial, geom = "geom", id = col_shape)
    mun_df <- fortify(mun_shp, region = col_shape)
    names(mun_df)[names(mun_df)=="id"] <- col_shape

    return(mun_df)
}

#' @title discon_db
#'
#' @description This function disconnects a PostgreSQL
#' connection.
#'
#' @param connection DBI connection. A connection to a database must be open and given.
#'
#' @examples discon_db(con)
#' @export
discon_db <- function(connection){
    RPostgreSQL::dbDisconnect(connection)
}

First we load the PLANEA results.

We load the geometries but will use them later

planea[logros] <- sapply(planea[logros],as.numeric)
NAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercion

We need to change some municipalities names in order to join this database with the CONAPO one. This are the missed-matched names in the tables of the database. I’ll change the ones from the schools.

planea$municipio <- plyr::mapvalues(planea$municipio, to=to_conapo, from=from_schools)
The following `from` values were not present in `x`: DR. BESLISARIO DOMINGUEZ

Load the CONAPO table

Load the marginalization table and change the variable names to usable ones.

clear_results(con)
[1] TRUE

Join the three tables. I should run this in Postgres. TODO write SQL statement.

This result is for a table in the document

How many schools

The number of schools in a municipality.

conapo$CLAVE %>% unique() %>% length()
[1] 2457

Fit porwer-law to number of schools

schools_power_law <- lm(log10(num_school) ~ log10(number),num_schools)
schools_power_law

Call:
lm(formula = log10(num_school) ~ log10(number), data = num_schools)

Coefficients:
  (Intercept)  log10(number)  
        3.007         -1.389  
the_p_law <- tibble(number=seq_schools,value=adjusted_power)
num_schools %<>% left_join(the_p_law,by="number")

Number of evaluated students in language

sum(as.numeric(planea$evaluados_len))
[1] 2032174

Number of evaluated students in mathematics

sum(as.numeric(planea$evaluados_mat))
[1] 2024534

There’s a variable called representativity for both math and language but it’s unclear the percentage cut-off, then I will use the schools with above 50% of both tests

schools <- schools[(schools$porc_len > 50 & schools$porc_mat > 50),]
mun_schools <- schools %>%
  select(CLAVE,escuela,POP,marginacion,gm,elementary,illiteracy) %>%
  unique() %>% dplyr::group_by(CLAVE) %>%
  dplyr::summarise(num_school = n(),POB=unique(POP),elementary=unique(elementary),illiteracy=unique(illiteracy),gm=unique(gm)) %>%
  mutate(spc = num_school/(POB/100000))

Number of evaluated students in language

sum(as.numeric(schools$evaluados_len))
[1] 4032596

Number of evaluated students in mathematics

sum(as.numeric(schools$evaluados_mat))
[1] 4018728
unique((schools$clave)) %>% length()
unique((schools$CLAVE)) %>% length()
schools_l <- schools %>% group_by(CLAVE, clave, escuela, gm) %>% summarise(num_school=n(),POP=unique(POP),illiteracy=unique(illiteracy),elementary=unique(elementary),I_len = median(I_porc_len),II_len = median(II_porc_len),III_len = median(III_porc_len),IV_len = median(IV_porc_len),
                                                    I_mat = median(I_porc_mat),II_mat = median(II_porc_mat),III_mat = median(III_porc_mat),IV_mat = median(IV_porc_mat))
`summarise()` has grouped output by 'CLAVE', 'clave', 'escuela'. You can override using the `.groups` argument.

Translate some variables to english. There are some with peculiar name that shouldn’t be translated.

Define the colors

altoc <- "#e3b23c"
bajoc <- "#725752"
medioc <- "#fe64a3"
maltoc <- "#78bc61"
mbajoc <- "#4f359b"

Define plot colors

comc <- "#efecca"
privc <- "#db2b39"
pubgc <- "#f7ff58"
pubtc <- "#ff934f"
telec <- "#29335c"

schools with 100% in IV level for mathematics

Load the indigenous population and joint information with the test.

Plot the percentage of indigenous population by level of marginalization.

Some variables that won’t make it into the document

As expected the ratio of indigenous population in a municipality correlates perfectly with the percentage of inhabitants that speak a indigenous language.

The more indigenous more illiteracy.

The percentage of indigenous population correlates possitevily with the percentage of level 1 achievement.

lm(I_len/100 ~ p_len, mar_scores)

While for the level IV it has a negative correlation

lm(IV_len/100 ~ p_len, mar_scores)

How does language and math level I correlate in a highly marginalized municipality?

what about a very low marginalized area?

lm((I_mat) ~ (I_len),mar_scores[mar_scores$gm == "Very Low",])

Call:
lm(formula = (I_mat) ~ (I_len), data = mar_scores[mar_scores$gm == 
    "Very Low", ])

Coefficients:
(Intercept)        I_len  
    29.0414       0.8352  
lm((I_mat) ~ (I_len),mar_scores[mar_scores$gm == "Very High",])

Call:
lm(formula = (I_mat) ~ (I_len), data = mar_scores[mar_scores$gm == 
    "Very High", ])

Coefficients:
(Intercept)        I_len  
    32.9009       0.6141  

mt <- ggplot(mar_scores)+
  geom_point(aes(log10(IV_len),log10(IV_mat),color=gm))+
  scale_color_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                      labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
  coord_fixed()+
  geom_abline()+
  scale_x_log10(name = "percentage language")+#,limits = c(0,100))+
  scale_y_log10(name = "percentage mathematics")+#,limits = c(0,100))+
  ggtitle("Achievement Level IV by marginalization")+
  th+
  theme(
    axis.text.x = element_text(angle = 0),
    panel.grid.minor = element_blank(),
    legend.position = "none")

mt + facet_grid(cols=vars(gm)) +
    theme(strip.background = element_rect(fill="#b53a31"))

ggsave("../../figs/math_vs_language_IV_log.png",height = 20, width = 40, units = "cm")
mt <- ggplot(mar_scores)+
  geom_point(aes((IV_len),(IV_mat),color=gm))+
  scale_color_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                      labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
  coord_fixed()+
  geom_abline()+
  scale_x_continuous(name = "percentage language")+#,limits = c(0,100))+
  scale_y_continuous(name = "percentage mathematics")+#,limits = c(0,100))+
  ggtitle("Achievement Level IV by marginalization")+
  th+
  theme(
    axis.text.x = element_text(angle = 0),
    panel.grid.minor = element_blank(),
    legend.position = "none")

mt + facet_grid(cols=vars(gm)) +
    theme(strip.background = element_rect(fill="#b53a31"))
lm((IV_mat) ~ (IV_len),mar_scores[mar_scores$gm == "Very Low",])
lm((IV_mat) ~ (IV_len),mar_scores[mar_scores$gm == "Very High",])
mt <- ggplot(mar_scores)+
  geom_point(aes((IV_len),(I_mat),color=gm))+
  scale_color_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                      labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
  coord_fixed()+
  geom_abline(intercept = 100, slope=-1)+
  # scale_x_continuous(name = "percentage language",limits = c(0,100))+
  # scale_y_continuous(name = "percentage mathematics",limits = c(0,100))+
  ggtitle("Achievement Level IV by marginalization")+
  th+
  theme(
    axis.text.x = element_text(angle = 0),
    panel.grid.minor = element_blank(),
    legend.position = "none")

mt + facet_grid(cols=vars(gm)) +
    theme(strip.background = element_rect(fill="#b53a31"))

# ggsave("~/Documents/UVM/courses/DS2/assignments/HW6/math_vs_language_IV.png",height = 20, width = 40, units = "cm")
mt <- ggplot(mar_scores)+
  geom_point(aes((prop_indi),(IV_len),color=gm),alpha=0.6)+
  # geom_density(aes(pm_analf),)+
  # geom_point(aes(ph_analf,prop_indi,color=gm),alpha=0.4)+
  scale_color_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                      labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
  # coord_fixed()+
  # geom_abline()+
  # scale_x_continuous(name = "percentage language",limits = c(0,1))+
  # scale_y_continuous(name = "percentage mathematics",limits = c(0,100))+
  ggtitle("Achievement Level IV by marginalization")+
  th+
  theme(
    axis.text.x = element_text(angle = 0),
    panel.grid.minor = element_blank(),
    legend.position = "none")

mt + facet_grid(cols=vars(gm)) +
    theme(strip.background = element_rect(fill="#b53a31"))

# ggsave("~/Documents/UVM/courses/DS2/assignments/HW6/math_vs_language_IV.png",height = 20, width = 40, units = "cm")

Maps and things

I defined a function to get the geometries in an easy way. TODO: update fortify with broom

indig_pop <- indig %>% left_join(conapo,by="CLAVE")
indig_pop %<>% mutate(p_indig = IPOB_INDI/POP)
municipios_pop <- geom_muni %>% left_join(indig_pop, by = c("CVEGEO"="CLAVE"))
geom_muni$CVEGEO %>% unique() %>% length()

We have 7 municipalities without indigenous information, I don’t know if it is because they were instituted between 2015 and 2018, which is the last CONAPO available data and the geometry data that I have. (2456 to 2463, as in 2020 there are 2464)

ggplot()+
  geom_polygon(data = municipios_pop,
               aes(long,lat,label=CVEGEO,group=group,
                   fill=p_indig*100),color="grey",size=0.1)+
  coord_map(projection = "mercator")+
  scale_fill_continuous(name="Percentage",low="white", high="#0f5a5e",
                       guide="colorbar",limits=c(0,100))+
  labs(title="Indigenous Population")+
  theme(#panel.grid.major.y = element_line(color = "gray"),
        panel.grid.major.y = element_blank(),
        text = element_text(color = "gray20"),
        panel.background = element_blank(),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank(),
        legend.box = "horizontal",
        legend.text = element_text(size = 10),
        plot.caption = element_text(hjust=0),
        plot.title = element_text(size = 16, face = "bold",hjust=0.39))

ggsave("../../figs/map_indigenous.png",height = 30, width = 30, units = "cm")
municipios_nal <- geom_muni %>% left_join(mar_scores, by = c("CVEGEO"="CLAVE"))

A tilt in the map allows a better stacking

# Shear/scale matrix [[2,1],[0,1]] obtained by some trial and error:
sm <- matrix(c(1,-0.3,0,1),2,2)
# Get transformed coordinates:
xy <- as.matrix(municipios_nal[,c("long","lat")]) %*% sm
# Add xy as extra columns in fortified data:
municipios_nal$x <- xy[,1]; municipios_nal$y = xy[,2]
min(municipios_nal$x)
max(municipios_nal$x)

max(municipios_nal$y) - min(municipios_nal$y)
min(municipios_nal$y)
mean(municipios_nal$y)

Stacked map for the 4 levels of math

and language

p <- ggplot() +
  geom_polygon(data = municipios_nal, aes(x, y,label=CVEGEO, group=group,fill=(I_len)),color="white",size=0.01)+
  annotate("text",x=min(municipios_nal$x)+10,y=mean(municipios_nal$y),color="gray35",label="Level I")+
  geom_polygon(data = municipios_nal, aes(x, y-14,label=CVEGEO, group=group,fill=(II_len)),color="white",size=0.01)+
  annotate("text",x=min(municipios_nal$x)+10,y=mean(municipios_nal$y-14),color="gray35",label="Level II")+
  geom_polygon(data = municipios_nal, aes(x, y-28,label=CVEGEO, group=group,fill=(III_len)),color="white",size=0.01)+
  annotate("text",x=min(municipios_nal$x)+10,y=mean(municipios_nal$y-28),color="gray35",label="Level III")+
  geom_polygon(data = municipios_nal, aes(x, y-42,label=CVEGEO, group=group,fill=(IV_len)),color="white",size=0.01)+
  annotate("text",x=min(municipios_nal$x)+10,y=mean(municipios_nal$y-42),color="gray35",label="Level IV")+
  labs(title="Language")+
  scale_fill_continuous(name = "Mean percentage",low="white", high="#5a5a0d", guide="colorbar")+#,na.value="#f2f2f2")+
  coord_map(projection = "mercator")+
  theme(#panel.grid.major.y = element_line(color = "gray"),
        panel.grid.major.y = element_blank(),
        text = element_text(color = "gray20"),
        panel.background = element_blank(),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank(),
        # legend.box = "horizontal",
        legend.text = element_text(size = 10),
        plot.caption = element_text(hjust=0),
        plot.title = element_text(size = 16, face = "bold",hjust=0.39))

ggsave("../../figs/stacked_language.png",p,height = 30, width = 30, units = "cm")

I want to compute the density of schools by square kilometer

query <- load_table(con,public,geom_muni)
muni_df <- query %>% retrieve_result()
clear_results(con)

Build the polygons and compute the area in Km^2

polman <- lapply(muni_df$WKT,rgeos::readWKT)
rgeos::gUnion(polman[[1]],polman[[2]])
length(polman)

the_areas <- sapply(polman,geosphere::areaPolygon)
areas_df <- tibble(CVEGEO=muni_df$CVEGEO,area=the_areas/1000000)

Now we need the number of Schools but not from the test, we’ll use the data from Consejo Nacional de Fomento Educativo (CONAFE) through the Censo de Escuelas, Maestros y Alumnos de Educación Básica y Especial (CEMABE) Nop, now I now that CONAFE is a program to contribute children in highly marginalized areas to conclude their basic comunitary education, with economical support to educational figures and scholarly supplies to students. It would be interesting to analize this into deep!!!

This is the number of schools in the planea set

unique(planea$clave) %>% length()
schools_mun <- schools %>% group_by(CLAVE) %>% summarise(num=n())
areas_df %<>% left_join(schools_mun,by=c("CVEGEO"="CLAVE"))
areas_df %<>% mutate(density = num/area)

The density is log-normal As well as the test results. Should consider this when doing regressions.

ggplot(areas_df)+
  geom_histogram(aes(log10(density)))+
  th
geom_mun_school <- geom_muni %>% left_join(areas_df, by = c("CVEGEO"))
library(latex2exp)
ggplot()+
  geom_polygon(data = geom_mun_school,
               aes(long,lat,label=CVEGEO,group=group,
                   fill=(density)),color="grey",size=0.01)+
  coord_map(projection = "mercator")+
  # scale_fill_continuous(name="Percentage",low="white", high="#4c1262", guide="colorbar")+#,limits=c(0,100))+
  scale_fill_gradient(trans="log10",name="Density",low="white", high="#4c1262", guide="colorbar",labels=c(0,0.5,1,1.5,2,2.5))+#,limits=c(0,100))+
  labs(title=TeX("Schools by $Km^2$"))+
  theme(#panel.grid.major.y = element_line(color = "gray"),
        panel.grid.major.y = element_blank(),
        text = element_text(color = "gray20"),
        panel.background = element_blank(),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank(),
        legend.box = "horizontal",
        legend.text = element_text(size = 10),
        plot.caption = element_text(hjust=0),
        plot.title = element_text(size = 16, face = "bold",hjust=0.39))

ggsave("../../figs/map_school_density.png",height = 30, width = 30, units = "cm")

I think I haven’t written the ingestion code for the schools into the sql database. TODO Although I’m not using it.

conafe <- read_delim("/home/ollin/Documents/UVM/courses/DS2/project/planea/data/clean/tr_conafe.csv",delim="|")
conafe %<>% mutate(CVEGEO=paste0(ENT,MUN))
conafe %>% group_by(CVEGEO) %>% summarise(num = n())

Maps are cool but we need other stuff, let’s make ridges for achievement

library(ggridges)
mar_tidy <- mar_scores %>% pivot_longer(c(I_len,II_len,III_len,IV_len,I_mat,II_mat,III_mat,IV_mat)) %>% unique()

First for all

ggplot(mar_tidy[grep("len",mar_tidy$name),], aes(x = value, y = name, height = stat(density))) +
  geom_density_ridges(stat = "density")+
  scale_x_continuous(name="Percentage")+
  scale_y_discrete(name="Level")+
  th
ggsave("../../figs/ridges_len.png",height = 30, width = 30, units = "cm")
ggplot(mar_tidy[grep("mat",mar_tidy$name),], aes(x = value, y = name, height = stat(density))) +
  geom_density_ridges(stat = "density")+
  scale_x_continuous(name="Percentage")+
  scale_y_discrete(name="Level")+
  th
ggsave("../../figs/ridges_math_log.png",height = 30, width = 30, units = "cm")

We conducted an Anderson-Darling test to assess the normality of distributions along achievement levels

compute how likely are them to be drawned by a normal distribution

This means that our values are not normal

compute how likely are them to be drawned by a the same distribution

combos <- combn(4,2)
plyr::adply(combos, 2, function(x) {
  test <- t.test(Datas[, as.numeric(x[1])], Data[, as.numeric(x[2])],alternative = "t")

  out <- data.frame("var1" = colnames(Data)[x[1]]
                    , "var2" = colnames(Data[x[2]])
                    , "t.value" = sprintf("%.5f", test$statistic)
                    ,  "df"= test$parameter
                    ,  "p.value" = test$p.value#sprintf("%.5f", test$p.value)
                    )
  return(out)

})
# mar_scores %>%
#   dplyr::select(I_len,II_len,III_len,IV_len) %>%
#   dplyr::select_if(is.numeric) %>%
#   purrr::map_df(~ broom::tidy(t.test(. ~ grp)), .id = 'var')
ggplot(mar_tidy[grep("len",mar_tidy$name),], aes(x = value, y = name, height = stat(density))) +
  geom_density_ridges(stat = "density")+
  scale_x_log10(name="Percentage")+
  scale_y_discrete(name="Level")+
  th

ggsave("../../figs/ridges_language_log.png",height = 30, width = 30, units = "cm")

compute how likely are them to be have the same mean

compute how likely are them to have the same mean

combos <- combn(4,2)
plyr::adply(combos, 2, function(x) {
  test <- t.test(Data[, as.numeric(x[1])], Data[, as.numeric(x[2])],alternative = "t")

  out <- data.frame("var1" = colnames(Data)[x[1]]
                    , "var2" = colnames(Data[x[2]])
                    , "t.value" = sprintf("%.5f", test$statistic)
                    ,  "df"= test$parameter
                    ,  "p.value" = test$p.value#sprintf("%.5f", test$p.value)
                    )
  return(out)

})
# mar_scores %>%
#   dplyr::select(I_len,II_len,III_len,IV_len) %>%
#   dplyr::select_if(is.numeric) %>%
#   purrr::map_df(~ broom::tidy(t.test(. ~ grp)), .id = 'var')

Then the facets by marginalization level

ggplot(mar_tidy[grep("mat",mar_tidy$name),], aes(x = value, y = name,fill=gm,alpha=0.6, height = stat(density))) +
  scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                      labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
  geom_density_ridges(stat = "density")+
  scale_x_log10()+
  th+
  theme(
    axis.text.x = element_text(angle = 0),
    panel.grid.minor = element_blank(),
    legend.position = "none")+
  facet_grid(~gm)+
  theme(strip.background = element_rect(fill="#b53a31"))
ggplot(mar_tidy[grep("len",mar_tidy$name),], aes(x = value, y = gm,fill=name,alpha=0.6, height = stat(density))) +
  scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                        labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
    geom_density_ridges(stat = "density")+
  scale_x_log10()+
    th+
    theme(
      axis.text.x = element_text(angle = 0),
      panel.grid.minor = element_blank(),
      legend.position = "none")+
    facet_grid(~name)+
    theme(strip.background = element_rect(fill="#b53a31"))
ggplot(mar_tidy[!grepl("II_",mar_tidy$name) & grepl("I_",mar_tidy$name),], aes(x = value, y = name,fill=gm,alpha=0.6, height = stat(density))) +
  scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                        labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
    geom_density_ridges(stat = "density")+
  scale_x_log10(name="Percentage")+
  scale_y_discrete(name=" ")+
    th+
    theme(
      axis.text.x = element_text(angle = 0),
      panel.grid.minor = element_blank(),
      legend.position = "none")+
    facet_grid(~gm)+
    theme(strip.background = element_rect(fill="#b53a31"))

ggsave("../../figs/ridges_marg_I.png",height = 15, width = 30, units = "cm")

We test the homogeneity of variances with an F-test because we want to see how well does an achievement level in one test explains the other.

tocomp <- mar_scores %>%
  group_by(gm) %>%
  select(gm,I_len,I_mat,IV_len,IV_mat)
logss <- sapply(tocomp %>% ungroup(gm) %>% select(-gm), function(x) log10(as.numeric(x)) )
tocomp[2:5] <- logss
tocomp <- tocomp[apply(tocomp %>% ungroup(gm) %>% select(-gm), 1, function(row) all(is.finite(row))),]
# tocomp <- tocomp %>% sample_n(250,replace = F)

tocomp %>%
  group_by(gm) %>%
  summarise(pval = var.test(I_len,I_mat,alternative="t")$p.value)
ggplot(mar_tidy[grepl("IV_",mar_tidy$name),], aes(x = (value), y = name,fill=gm,alpha=0.6, height = stat(density))) +
  scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                        labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
    geom_density_ridges(stat = "density")+
  scale_x_log10(name="Percentage")+
  scale_y_discrete(name=" ")+
    th+
    theme(
      axis.text.x = element_text(angle = 0),
      panel.grid.minor = element_blank(),
      legend.position = "none")+
    facet_grid(~gm)+
    theme(strip.background = element_rect(fill="#b53a31"))
ggsave("../../figs/ridges_marg_IV.png",height = 15, width = 30, units = "cm")

Use the Fligner-Killen median test to compare the homogeneity of variances

tocomp <- mar_scores %>%
  group_by(gm) %>%
  select(gm,I_len,I_mat,IV_len,IV_mat)
logss <- sapply(tocomp %>% ungroup(gm) %>% select(-gm), function(x) log10(as.numeric(x)) )
tocomp[2:5] <- logss
tocomp <- tocomp[apply(tocomp %>% ungroup(gm) %>% select(-gm), 1, function(row) all(is.finite(row))),]
# tocomp <- tocomp %>% sample_n(300,replace = F)

tocomp %>%
  group_by(gm) %>%
  summarise(pval = var.test(IV_len,IV_mat,alternative="t")$p.value)
tocomp <- schools[!is.na(schools$gm),] %>%
  group_by(gm) %>%
  select(gm,I_porc_len,I_porc_mat,IV_porc_len,IV_porc_mat)
logss <- sapply(tocomp %>% ungroup(gm) %>% select(-gm), function(x) log10(as.numeric(x)) )
tocomp[2:5] <- logss
tocomp <- tocomp[apply(tocomp %>% ungroup(gm) %>% select(-gm), 1, function(row) all(is.finite(row))),]
# tocomp <- tocomp %>% sample_n(250,replace = F)

tocomp %>%
  group_by(gm) %>%
  summarise(pval = var.test(IV_porc_len,IV_porc_mat,alternative="t")$p.value)

But what about school type

mar_tidy <- mar_scores %>% pivot_longer(c(I_len,II_len,III_len,IV_len,I_mat,II_mat,III_mat,IV_mat)) %>% unique()
mar_scores %<>% left_join(schools %>% select(CLAVE,tipo_escuela),by="CLAVE") %>% unique()
mar_tidy2 <- mar_scores %>% pivot_longer(c(I_len,II_len,III_len,IV_len,I_mat,II_mat,III_mat,IV_mat)) %>% unique()
ggplot(mar_tidy2[grepl("I_",mar_tidy2$name),], aes(x = (value), y = name,fill=tipo_escuela,alpha=0.6, height = stat(density))) +
  # scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
  #                       labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
    geom_density_ridges(stat = "density")+
  scale_x_log10()+
    th+
    theme(
      axis.text.x = element_text(angle = 0),
      panel.grid.minor = element_blank(),
      legend.position = "none")+
    facet_grid(~tipo_escuela)+
    theme(strip.background = element_rect(fill="#b53a31"))

Build the distribution difference variable

schools$I_porc_mat - schools$I_porc_len
schools %<>% mutate(diffI = I_porc_mat - I_porc_len, diffIV = IV_porc_mat - IV_porc_len)
sch_tidy <- schools %>% pivot_longer(c(diffI, diffIV)) %>% unique()
ggplot(sch_tidy, aes(x = (value), y = name,fill=gm,alpha=0.6, height = stat(density))) +
  scale_fill_manual(name = "Marginalization", values = c("High" = altoc, "Low" = bajoc, "Medium" = medioc, "Very High" = maltoc, "Very Low" = mbajoc),
                        labels = c("Very Low", "Low", "Medium", "High", "Very High"))+
    geom_density_ridges(stat = "density")+
  scale_x_log10(name="Percentage")+
  scale_y_discrete(name=" ")+
    th+
    theme(
      axis.text.x = element_text(angle = 0),
      panel.grid.minor = element_blank(),
      legend.position = "none")+
    facet_grid(~gm)+
    theme(strip.background = element_rect(fill="#b53a31"))

ggplot(schools[complete.cases(schools$gm),],aes(diffIV,fill=gm))+
  geom_density()

ggplot(schools[!is.na(schools$gm),],aes(x=(diffIV+100)/200,y=gm,fill=gm,height = stat(density)))+
  geom_density_ridges(stat="density")+
  scale_x_log10()


# ggsave("../../figs/ridges_marg_IV.png",height = 15, width = 30, units = "cm")
ggplot(schools[!is.na(schools$gm),],aes(x=(diffI+100)/200,y=gm,fill=gm,height = stat(density)))+
  geom_density_ridges(stat="density")+
  scale_x_log10()#limits = c(-50,50))

mate - lengua

ci_median <- function(x){
  bootmed = apply(matrix(sample(x, rep=TRUE, 10^4*length(x)), nrow=10^3), 1, median)
  quantile(bootmed, c(.025,0.5, 0.975))
}

schools[complete.cases(schools$gm),] %>% group_by(gm) %>% summarise(median(diffI),median(diffIV))
schools %>% group_by(gm) %>% summarise(ci_median(log10((diffIV+100)/200))[2])
schools[complete.cases(schools$gm),] %>% group_by(gm) %>% summarise(median(diffI),ci_median(diffIV)[1],ci_median(diffIV)[3])

Let’s check the Bayesian networks

Puerto Morelos became municipality in 2011

encode_ordinal <- function(x, order = unique(x)) {
  x <- as.numeric(factor(x, levels = order, exclude = NULL))
  x
}
schools <- schools %>% select(-ovsd,-ovsdse,-ind0a100)
schools <- schools[complete.cases(schools),]
areas_df <- areas_df %>% mutate(CLAVE=CVEGEO) %>% select(-CVEGEO)
indig
for_bn <- schools %>% left_join(indig %>% select(CLAVE,IPOB_INDI,IPhli))
for_bn <- for_bn %>% mutate(p_indig = IPOB_INDI/pop_marg,lang_indig=IPhli/IPOB_INDI)
for_bn %<>% left_join(areas_df,by="CLAVE")

for_bn[!complete.cases(for_bn),]$IPOB_INDI <- 0
for_bn[!complete.cases(for_bn),]$p_indig <- 0
databn <- for_bn %>% ungroup(CLAVE,NOM_ENT) %>%
  select(diffI,diffIV,turno,gm,tipo_escuela,illiteracy,elementary,no_sewage,no_electricity,no_water,overcrowding,dirt_floor,less_5k,less_2minwage,im,lugar,lugarest,IPOB_INDI,p_indig,area,num,density,lang_indig)
  # select(I_porc_len,II_porc_len,III_porc_len,IV_porc_len,I_porc_mat,II_porc_mat,III_porc_mat,IV_porc_mat,turno,gm,tipo_escuela,illiteracy,elementary,no_sewage,no_electricity,no_water,overcrowding,dirt_floor,less_5k,less_2minwage,im,lugar,lugarest,IPOB_INDI,p_indig,area,num,density,lang_indig)

databn

# I should automate this
databn$turno <- encode_ordinal(databn$turno)
databn$gm <- encode_ordinal(databn$gm)
databn$tipo_escuela <- encode_ordinal(databn$tipo_escuela)

# databn$turno <- as.double(databn$turno)
# databn$gm <- as.double(databn$gm)
# databn$num <- as.double(databn$num)
# databn$tipo_escuela <- as.double(databn$tipo_escuela)

databn2 <- databn

databn$I_porc_len <- log10(databn$I_porc_len)
databn$II_porc_len <- log10(databn$II_porc_len)
databn$III_porc_len <- log10(databn$III_porc_len)
databn$IV_porc_len <- log10(databn$IV_porc_len)

databn$I_porc_mat <- log10(databn$I_porc_mat)
databn$II_porc_mat <- log10(databn$II_porc_mat)
databn$III_porc_mat <- log10(databn$III_porc_mat)
databn$IV_porc_mat <- log10(databn$IV_porc_mat)

databn$density <- log10(databn$density)
databn$p_indig <- log10(databn$p_indig)
databn$lang_indig <- log10(databn$lang_indig)

databn_finite <- databn[apply(databn, 1, function(row) all(is.finite(row))),]
databn_finite <- databn[apply(databn %>% select(-turno,-gm,-num,-tipo_escuela), 1, function(row) all(is.finite(row))),]
databn_finite %<>% select(-im,-lugar,-lugarest,-IPOB_INDI,-area,-num)

Delete the highly correlated variables

set.seed(42)
# corrM <- cor(databn_finite[,c(3:17)])
corrM <- cor(databn_finite)
highlyCorrelated <- findCorrelation(corrM, cutoff=0.70)
colnames(databn_finite[,c(3:17)])[highlyCorrelated]
databn_finite %<>% select(-illiteracy,-elementary,-overcrowding,-dirt_floor,-less_5k)
databn_finite %<>% select(-gm)

Variable selection

# control <- trainControl(method="cv", number=10)
# model <- caret::train(I_porc_len~., data=databn_finite, method="cforest", preProcess="scale", trControl=control)
# # estimate variable importance
# importance <- varImp(model, scale=FALSE)
# # summarize importance
# print(importance)
# # plot importance
# plot(importance)
# set.seed(42)
# databn_finite2 <- as.data.frame(databn_finite)
# #Controlling the feature selection, using default functions, with 10 folds cross validation
# control <- rfeControl(functions=rfFuncs, method="cv", number=10)
# # run the RFE algorithm
# # using recursive feature elimination or backwards selection
# # let's check first if it works the same for each level and test
# results <- rfe(x=databn_finite2[,c(1,10:19)], y=databn_finite2[,2], rfeControl=control)
# # results <- rfe(databn[,c(1,10:27)], databn[,n], sizes=c(1:18), rfeControl=control)
# # summarize the results
# print(results)
# # list the chosen features
# predictors(results)
# # plot the results
# plot(results, type=c("g", "o"))

I haven’t figured how to save this graphs with the chunk output inline in a notebook, so we have to dissable it to run and save the plots

for (i in 1:7){
  bnf <- bnlearn::h2pc(databn_finite[,c(i,9:17)])
  png(paste0("/figs/graph_",i,".png"))
  plot(graphviz.plot(bnf, shape = "ellipse"))
  dev.off()
  str.diff <- bnlearn::boot.strength(databn_finite[,c(i,9:17)],R=20,algorithm="h2pc")
  avg.diff <- averaged.network(str.diff)#,threshold = 0.7)
  png(paste0("/figs/graph_",i,"_boots.png"))
  plot(graphviz.plot(avg.diff, shape = "ellipse"))
  dev.off()
}
# the_columns <- c(diffI, turno, tipo_escuela, illiteracy, elementary, no_sewage, no_electricity, no_water, overcrowding, dirt_floor, less_5k, less_2minwage, im, p_indig, density, lang_indig)
# bnf <- bnlearn::h2pc(databn_finite[,c(1,3:11)])
bnf <- bnlearn::h2pc(databn_finite %>% select(diffI, turno, tipo_escuela, illiteracy, elementary, no_sewage, no_electricity, no_water, overcrowding, dirt_floor, less_5k, less_2minwage, im, p_indig, density, lang_indig))

# str.diff <- bnlearn::boot.strength(databn_finite[,c(1,3:11)],R=20,algorithm="h2pc")
str.diff <- bnlearn::boot.strength(databn_finite %>% select(diffI, turno, tipo_escuela, illiteracy, elementary, no_sewage, no_electricity, no_water, overcrowding, dirt_floor, less_5k, less_2minwage, im, p_indig, density, lang_indig),R=20,algorithm="h2pc")

avg.diff <- averaged.network(str.diff)#,threshold = 0.7)
graphviz.plot(avg.diff, shape = "ellipse")
bnf <- bnlearn::h2pc(databn_finite[,c(2,3:11)])
  str.diff <- bnlearn::boot.strength(databn_finite[,c(2,3:11)],R=20,algorithm="h2pc")
  avg.diff <- averaged.network(str.diff)#,threshold = 0.7)
  graphviz.plot(avg.diff, shape = "ellipse")
bnf <- bnlearn::h2pc(databn_finite[,c(1,9:17)])
fitted <- bn.fit(bnf,databn_finite[,c(1,9:17)])
graphviz.plot(bnf, shape = "ellipse")
bnf <- bnlearn::h2pc(databn_finite[,c(1,3:11)])
bnf <- bnlearn::reverse.arc(bnf,from="tipo_escuela",to="IV_porc_len")
bnf <- bnlearn::reverse.arc(bnf,from="turno",to="IV_porc_len")
bnf <- bnlearn::reverse.arc(bnf,from="density",to="IV_porc_len")
fitted <- bn.fit(bnf,databn_finite[,c(4,3:11)])
graphviz.plot(bnf, shape = "ellipse")
bnf <- bnlearn::h2pc(databn_finite[,c(2,3:11)])
bnf <- bnlearn::reverse.arc(bnf,from="tipo_escuela",to="I_porc_mat")
bnf <- bnlearn::reverse.arc(bnf,from="turno",to="I_porc_mat")
bnf <- bnlearn::reverse.arc(bnf,from="overcrowding",to="I_porc_mat")
bnf <- bnlearn::reverse.arc(bnf,from="lang_indig",to="I_porc_mat")
fitted <- bn.fit(bnf,databn_finite[,c(2,3:11)])
graphviz.plot(bnf, shape = "ellipse")
bnf <- bnlearn::h2pc(databn_finite[,c(8,9:17)])
bnf <- bnlearn::reverse.arc(bnf,from="tipo_escuela",to="IV_porc_mat")
bnf <- bnlearn::reverse.arc(bnf,from="turno",to="IV_porc_mat")
fitted <- bn.fit(bnf,databn_finite[,c(8,9:17)])

bn.fit.qqplot(fitted)
bn.fit.barchart(fitted$IV_porc_mat)
bn.cv(databn_finite[,c(1,9:17)],bn="h2pc")
bn.cv(databn_finite[,c(4,9:17)],bn="h2pc")

bn.cv(databn_finite[,c(5,9:17)],bn="h2pc")
a<-bn.cv(databn_finite[,c(8,9:17)],bn="h2pc")
graphviz.plot(bnf, shape = "ellipse")

1 - mat log likelihood 14.78539
4 - mat log likelihood 15.17735

1 - lan log likelihood 14.93725
4 - lan log likelihood 14.79238
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojICBUaGlzIGlzIGZvciB0aGUgbGF0ZXN0IDIwMTkgUExBTkVBIHRlc3QhCgpgYGB7cixlY2hvPUZBTFNFLHdhcm5pbmc9RkFMU0UsaW5jbHVkZT1GQUxTRX0KcGFxdWV0aW5lcyA8LSBjKCJyZWFkeGwiLCJtYWdyaXR0ciIsImRwbHlyIiwiZ2dwbG90MiIsInRpZHlyIiwicmVhZHIiLCJzdHJpbmdyIiwiZnV6enlqb2luIiwicnN0YW4iLCJEQkkiLCJSUG9zdGdyZVNRTCIsInJhbmdlTWFwcGVyIiwiYm5sZWFybiIsIkJpb2NNYW5hZ2VyIiwibWFwcHJvaiIpCm5vX2luc3RhbGFkb3MgPC0gcGFxdWV0aW5lc1shKHBhcXVldGluZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXSldCmlmKGxlbmd0aChub19pbnN0YWxhZG9zKSkgaW5zdGFsbC5wYWNrYWdlcyhub19pbnN0YWxhZG9zKQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiUmdyYXBodml6IikKbGFwcGx5KHBhcXVldGluZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKbGlicmFyeShSZ3JhcGh2aXopCmBgYAoKCkVhc3kgZ2dwbG90IHRoZW1lLgpgYGB7cixlY2hvPUZBTFNFLHdhcmluZz1GQUxTRX0KdGggPC0gdGhlbWVfbWluaW1hbCgpKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5IiksCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmF5MjAiKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIixzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJpdGFsaWMiLHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLAogICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0PTApLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLGhqdXN0PTAuMzkpKQpgYGAKCgoKTGV0J3MgZGVmaW5lIHNvbWUgZnVuY3Rpb24gdG8gaW50ZXJhY3QgZWFzaWx5IHdpdGggdGhlIGRhdGFiYXNlLgpgYGB7cn0KIycgQHRpdGxlIHBsYW5fY29ubmVjdAojJwojJyBAZGVzY3JpcHRpb24gVGhpcyBmdW5jdGlvbiB0YWtlcyB0aGUgUG9zdGdyZXMgZW52aXJvbm1lbnQgdmFyaWFibGVzCiMnIHRvIGF1dG9tYXRpY2FsbHkgY29ubmVjdCB0byB0aGUgcGxhbmVhZGIgZGF0YWJhc2UuCiMnIFRoZSBjb25uZWN0aW9uIG11c3QgYmUgc2F2ZWQgaW50byBhIHZhcmlhYmxlIGluIG9yZGVyIHRvCiMnIHVzZSBmdXR1cmUgZnVuY3Rpb25zLiBJJ20gY2hlYXRpbmcgYW5kIG5vdCByZWFkaW5nIGZyb20gLmVudi4KIycKIycgQGV4YW1wbGVzIGNvbiA8LSBwcmV2X2Nvbm5lY3QoKQojJwojJyBAcGFyYW0gTm8gcGFyYW1ldGVyIGlzIG5lZWRlZC4KIycgQGV4cG9ydApwbGFuX2Nvbm5lY3QgPC0gZnVuY3Rpb24oKXsKICBEQkk6OmRiQ29ubmVjdChSUG9zdGdyZVNRTDo6UG9zdGdyZVNRTCgpLAogIGhvc3QgICAgID0gICJsb2NhbGhvc3QiLAogIHVzZXIgICAgID0gICJwbGFuZWEiLAogIHBhc3N3b3JkID0gICJwbGFuZWEiLAogIHBvcnQgICAgID0gIDU0MzIsCiAgZGJuYW1lICAgPSAgInBsYW5lYSIpCn0KCiMnIEB0aXRsZSBsb2FkX3F1ZXJ5CiMnCiMnIEBkZXNjcmlwdGlvbiBBbGxvd3MgdG8gcnVuIHNwZWNpZmljIHF1ZXJpZXMgd2l0aCB0aGUgZGJyc29jaWFsIHN5bnRheC4KIycKIycgQHBhcmFtIGNvbm5lY3Rpb24gREJJIGNvbm5lY3Rpb24uIEEgY29ubmVjdGlvbiB0byBhIGRhdGFiYXNlCiMnIEBwYXJhbSBzY2hlbWEgdmFyaWFibGUuIEEgdmFsaWQgc2NoZW1hIGZyb20gYSBkYXRhYmFzZSBvbiB0aGUKIycgQHBhcmFtIHRoZV90YWJsZS4gIEFuIGV4aXN0aW5nIHRhYmxlIGluIHRoZSBnaXZlbiBzY2hlbWEuCiMnIEBwYXJhbSBjb2x1bXMgc3RyaW5nLiBUaGUgY29sdW1ucyBpbiB0aGUgZGF0YWJhc2Ugd2Ugd2FudCB0byByZXRyaWV2ZQojJyBpbmZvcm1hdGlvbgojJyBAcGFyYW0gb3B0aW9ucyBzdHJpbmcuIFBhcnQgb2YgdGhlIFNRTCBxdWVyeSB3aXRoIGNvbnRhaW5pbmcgV0hFUkUsIE9SREVSLAojJyBMSU1JVCBhbmQgc28gc3RhdGVtZW50cwojJwojJyBAZXhhbXBsZXMgZ2VvbV9tdW5pIDwtCiMnIGxvYWRfcXVlcnkoY29uLHJhdyxzaWZvZGUsY29sdW1ucz0iZW50aWRhZGZlZGVyYXRpdmEiLG9wdGlvbnM9IldIRVJFCiMnIG1lcz0nQWJyaWwnIikKIycgQGV4cG9ydApsb2FkX3F1ZXJ5IDwtIGZ1bmN0aW9uKGNvbm5lY3Rpb24sc2NoZW1hLHRoZV90YWJsZSxjb2x1bW5zPSIqIixvcHRpb25zPSIiKXsKICAgIHRoZV9xdWVyeSA8LSAiU0VMRUNUICVzIEZST00gJXMuJXMiCiAgICBjb21wbGV0ZSA8LSBwYXN0ZTAodGhlX3F1ZXJ5LCIgIixvcHRpb25zKQogICAgc2NoZW1hICAgIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShzY2hlbWEpKQogICAgdGhlX3RhYmxlIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZSh0aGVfdGFibGUpKQogICAgaW5pdGlhbCA8LSBSUG9zdGdyZVNRTDo6ZGJTZW5kUXVlcnkoY29ubmVjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKGNvbXBsZXRlLGNvbHVtbnMsc2NoZW1hLHRoZV90YWJsZSkpCiAgICByZXR1cm4oaW5pdGlhbCkKfQoKIycgQHRpdGxlIGxvYWRfdGFibGUKIycKIycgQGRlc2NyaXB0aW9uIFRoaXMgZnVuY3Rpb24gbG9hZHMgYSBjb25uZWN0aW9uIHRvCiMnIGEgZ2l2ZW4gdGFibGUgZnJvbSBhIHBhcnRpY3VsYXIgc2NoZW1hIGluIGEgZGF0YWJhc2UKIycgY29ubmVjdGlvbi4KIycKIycgQHBhcmFtIGNvbm5lY3Rpb24gREJJIGNvbm5lY3Rpb24uIEEgY29ubmVjdGlvbiB0byBhIGRhdGFiYXNlCiMnIG11c3QgYmUgb3BlbiBhbmQgZ2l2ZW4uCiMnIEBwYXJhbSBzY2hlbWEgdmFyaWFibGUuIEEgdmFsaWQgc2NoZW1hIGZyb20gYSBkYXRhYmFzZSBvbiB0aGUKIycgY29ubmVjdGVkIGRhdGFiYXNlLgojJyBAcGFyYW0gdGhlX3RhYmxlLiBBbiBleGlzdGluZyB0YWJsZSBpbiB0aGUgZ2l2ZW4gc2NoZW1hLgojJwojJyBAZXhhbXBsZXMgdGhlX2RpYzwtbG9hZF90YWJsZShjb24scmF3LHNpZm9kZV9kaWMpCiMnIEBleHBvcnQKbG9hZF90YWJsZSA8LSBmdW5jdGlvbihjb25uZWN0aW9uLHNjaGVtYSx0aGVfdGFibGUpewogICAgdGhlX3F1ZXJ5IDwtICJTRUxFQ1QgKiBGUk9NICVzLiVzIgogICAgc2NoZW1hICAgIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShzY2hlbWEpKQogICAgdGhlX3RhYmxlIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZSh0aGVfdGFibGUpKQogICAgaW5pdGlhbCA8LSBSUG9zdGdyZVNRTDo6ZGJTZW5kUXVlcnkoY29ubmVjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKHRoZV9xdWVyeSxzY2hlbWEsdGhlX3RhYmxlKSkKfQoKIycgQHRpdGxlIGxhcmdlX3RhYmxlCiMnCiMnIEBkZXNjcmlwdGlvbiBUaGlzIGZ1bmN0aW9uIGxvYWRzIGEgY29ubmVjdGlvbiB0byBhIGxhcmdlIHRhYmxlIHdpdGhvdXQKIycgbG9hZGluZyBpdCB0byBtZW1vcnkuCiMnCiMnIEBwYXJhbSBjb25uZWN0aW9uIERCSSBjb25uZWN0aW9uLiBBIGNvbm5lY3Rpb24gdG8gYSBkYXRhYmFzZQojJyBtdXN0IGJlIG9wZW4gYW5kIGdpdmVuLgojJyBAcGFyYW0gc2NoZW1hIHZhcmlhYmxlLiBBIHZhbGlkIHNjaGVtYSBmcm9tIGEgZGF0YWJhc2Ugb24gdGhlCiMnIGNvbm5lY3RlZCBkYXRhYmFzZS4KIycgQHBhcmFtIHRoZV90YWJsZS4gQW4gZXhpc3RpbmcgdGFibGUgaW4gdGhlIGdpdmVuIHNjaGVtYS4KIycKIycgQGV4YW1wbGVzIGN1aXNfdGFibGUgPC0gbGFyZ2VfdGFibGUoY29uLHJhdyxjdWlzXzM5XzkpCiMnIEBleHBvcnQKbGFyZ2VfdGFibGUgPC0gZnVuY3Rpb24oY29ubmVjdGlvbixzY2hlbWEsdGhlX3RhYmxlKXsKICAgIHNjaGVtYSAgICA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoc2NoZW1hKSkKICAgIHRoZV90YWJsZSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUodGhlX3RhYmxlKSkKICAgIHJldHJpZXZlZCA8LSBkcGx5cjo6dGJsKGNvbm5lY3Rpb24sZGJwbHlyOjppbl9zY2hlbWEoc2NoZW1hLHRoZV90YWJsZSkpCiAgICByZXR1cm4ocmV0cmlldmVkKQp9CgojJyBAdGl0bGUgY2xlYXJfcmVzdWx0cwojJwojJyBAZGVzY3JpcHRpb24gVGhpcyBmdW5jdGlvbiBjbGVhcnMgdGhlIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIGV4ZWN1dGVkCiMnIHF1ZXJ5LgojJwojJyBAcGFyYW0gY29ubmVjdGlvbiBEQkkgY29ubmVjdGlvbi4gQSBjb25uZWN0aW9uIHRvIGEgZGF0YWJhc2UgbXVzdCBiZSBvcGVuIGFuZCBnaXZlbi4KIycKIycgQGV4YW1wbGVzIGNsZWFyX3Jlc3VsdHMoY29uKQojJyBAZXhwb3J0CmNsZWFyX3Jlc3VsdHMgPC0gZnVuY3Rpb24oY29ubmVjdGlvbil7CiAgICBEQkk6OmRiQ2xlYXJSZXN1bHQoREJJOjpkYkxpc3RSZXN1bHRzKGNvbm5lY3Rpb24pW1sxXV0pCn0KCiMnIEB0aXRsZSBqb2luX3RhYmxlcwojJwojJyBAZGVzY3JpcHRpb24gUmV0dXJucyBhIG1hdGNoIGJldHdlZW4gdHdvIHRibCBieSBkZWZpbmVkIGtleQojJyBAcGFyYW0gbGVmdF90YWJsZSBhIHRibC1saWtlIG9iamVjdAojJyBAcGFyYW0gcmlnaHRfdGFibGUgYSB0YmwtbGlrZSBvYmplY3QKIycgQHBhcmFtIGxlZnRfa2V5IHRoZSBjb2x1bW4gbmFtZSBmcm9tIGxlZnRfdGFibGUgdG8gY29tcGFyZQojJyBAcGFyYW0gcmlnaHRfa2V5IHRoZSBjb2x1bW4gbmFtZSBmcm9tIHJpZ2h0X3RhYmxlIHRvIGNvbXBhcmUKIycKIycgQGV4YW1wbGVzIGpvaW5fdGFibGVzKGN1aXNfc2FtcGxlLGxsYXZlX2hvZ2FyX2gsZG9taWNpbGlvc19zYW1wbGVfcXVlcnksbGxhdmVfaG9nYXJfaCkKIycgQGV4cG9ydApqb2luX3RhYmxlcyA8LSBmdW5jdGlvbihsZWZ0X3RhYmxlLCBsZWZ0X2tleSwgcmlnaHRfdGFibGUsIHJpZ2h0X2tleSl7CiAgICBsZWZ0X2tleSA8LSBzdWJzdGl0dXRlKGxlZnRfa2V5KQogICAgcmlnaHRfa2V5IDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShyaWdodF9rZXkpKQogICAgaW5fdGFibGVzIDwtIGxlZnRfdGFibGUgJT4lCiAgICAgICAgZHBseXI6OmZpbHRlcihsZWZ0X2tleSAlaW4lIHJpZ2h0X3RhYmxlW1tyaWdodF9rZXldXSkKICAgIHJldHVybihpbl90YWJsZXMpCn0KCldLVDJTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUgPC0gZnVuY3Rpb24oZGF0LCBnZW9tLCBpZCkgewoJZGwgPSBzcGxpdChkYXQsIGRhdFssIGlkXSkKCglvID0gbGFwcGx5KGRsLCBmdW5jdGlvbih4KSB7CgoJCXAgPSBtYXBwbHkocmdlb3M6OnJlYWRXS1QsIHRleHQgPSB4WywgZ2VvbV0sIGlkID0gMTpucm93KHgpLCBVU0UuTkFNRVMgPSBGQUxTRSApCgkJCWlmKGxlbmd0aChwKSA9PSAxKSB7CgkJCQlwID0gcFtbMV1dCgkJCQlwID0gc3BDaEZJRHMocCwgYXMuY2hhcmFjdGVyKHhbMSwgaWRdKSkKCQkJCX0KCgkJCWlmKGxlbmd0aChwKSA+IDEpIHsKCQkJCXAgPSBkby5jYWxsKHNwOjpyYmluZC5TcGF0aWFsUG9seWdvbnMsIHApCgkJCQlwID0gcmdlb3M6OmdVbmlvbkNhc2NhZGVkKHAsIGlkID0gYXMuY2hhcmFjdGVyKHhbMSwgaWRdKSApCgkJCQl9CglwCgl9KQoKCVggPSBkby5jYWxsKHNwOjpyYmluZC5TcGF0aWFsUG9seWdvbnMsIG8pCglkYXQgPSBkYXRhLmZyYW1lKGlkID0gc2FwcGx5KHNsb3QoWCwgInBvbHlnb25zIiksIGZ1bmN0aW9uKHgpIHNsb3QoeCwgIklEIikpICkKCXJvdy5uYW1lcyhkYXQgKSA9IGRhdCRpZAoJbmFtZXMoZGF0KSA9IGlkCglYID0gc3A6OlNwYXRpYWxQb2x5Z29uc0RhdGFGcmFtZShYLCBkYXRhID0gIGRhdCkKCVgKCX0KCiMnIEB0aXRsZSByZXRyaWV2ZV9yZXN1bHQKIycKIycgQGRlc2NyaXB0aW9uIFJldHVybiB0aGUgZmV0Y2ggcmVzdWx0cyBvZiBhIHF1ZXJ5CiMnIEBwYXJhbSBxdWVyeSBBbiBleGVjIHVuZmV0Y2hlZCBxdWVyeQojJwojJyBAZXhhbXBsZXMgc2FtcGxlX3RhYmxlKGxvYWRfdGFibGUocHJldl9jb25uZWN0KCkscmF3LHNpZm9kZSkpCiMnIEBleHBvcnQKcmV0cmlldmVfcmVzdWx0IDwtIGZ1bmN0aW9uKHF1ZXJ5LG49LTEsbnVtYmVyPUluZil7CiAgICBpZiAoY2xhc3MocXVlcnkpWzFdID09ICJ0YmxfZGJpIil7CiAgICAgICAgdGhlX3RhYmxlIDwtIGRwbHlyOjpjb2xsZWN0KHF1ZXJ5LG49bnVtYmVyKQogICAgICAgIHJldHVybih0aGVfdGFibGUpCiAgICB9CiAgICBlbHNlewogICAgdGhlX3RhYmxlIDwtIERCSTo6ZGJGZXRjaChxdWVyeSxuKQogICAgcmV0dXJuKHRoZV90YWJsZSkKICAgIH0KfQoKIycgQHRpdGxlIGxvYWRfZ2VvbQojJwojJyBAZGVzY3JpcHRpb24gR2l2ZXMgYSAicmVhZHkgdG8gZ28iIGRhdGEgZnJhbWUgZm9yIGdlb21ldHJ5IHBsb3R0aW5nCiMnCiMnIEBwYXJhbSBjb25uZWN0aW9uIERCSSBjb25uZWN0aW9uLiBBIGNvbm5lY3Rpb24gdG8gYSBkYXRhYmFzZQojJyBAcGFyYW0gc2NoZW1hIHZhcmlhYmxlLiBBIHZhbGlkIHNjaGVtYSBmcm9tIGEgZGF0YWJhc2Ugb24gdGhlCiMnIEBwYXJhbSB0aGVfdGFibGUuICBBbiBleGlzdGluZyB0YWJsZSBpbiB0aGUgZ2l2ZW4gc2NoZW1hLgojJyBAcGFyYW0gY29sdW1zIHN0cmluZy4gVGhlIGNvbHVtbnMgaW4gdGhlIGRhdGFiYXNlIHdlIHdhbnQgdG8gcmV0cmlldmUuCiMnIERlZmF1bHRzIGN2ZV9lbnQsIGN2ZV9tdW4sIGN2ZV9tdW5pLgojJyBAcGFyYW0gZ2VvbV9jb2wgLiBUaGUgbmFtZSBvZiB0aGUgY29sdW1uIGluIHRoZSBkYXRhYmFzZSB0aGF0IGNvbnRhaW5zIGEgZ2VvbWV0cnkKIycgQHBhcmFtIGNvbF9zaGFwZS4gVGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB0aGF0IHdlIHdhbnQgdG8gdXNlIHRvIGpvaW4KIycgaW5mb3JtYXRpb24KIycgQHBhcmFtIG9wdGlvbnMgc3RyaW5nLiBQYXJ0IG9mIHRoZSBTUUwgcXVlcnkgd2l0aCBjb250YWluaW5nIFdIRVJFLCBPUkRFUiwKIycgTElNSVQgYW5kIHNvIHN0YXRlbWVudHMKIycKIycgQGV4YW1wbGVzIGdlb21fbXVuaSA8LSBsb2FkX2dlb20oY29uMSxyYXcsZ2VvbV9tdW5pY2lwaW9zLGdlb21fY29sPWdlb20sY29sX3NoYXBlPWN2ZV9tdW5pLG9wdGlvbnM9b3B0aW9ucykKIycgQGV4cG9ydApsb2FkX2dlb20gPC0gZnVuY3Rpb24oY29ubmVjdGlvbixzY2hlbWEsdGhlX3RhYmxlLGNvbHVtbnM9IlwiQ1ZFR0VPXCIsIFwiQ1ZFX0VOVFwiLCBcIkNWRV9NVU5cIiIsIGdlb21fY29sLCBjb2xfc2hhcGUsIG9wdGlvbnM9IiIpewogICAgZ2VvbV9jb2wgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGdlb21fY29sKSkKICAgIHNjaGVtYSAgICA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoc2NoZW1hKSkKICAgIHRoZV90YWJsZSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUodGhlX3RhYmxlKSkKICAgIGNvbF9zaGFwZSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29sX3NoYXBlKSkKICAgIGdlb21fY29sMiA8LSBwYXN0ZTAoIlwiIixnZW9tX2NvbCwiXCIiKQoKICAgIHRoZV9xdWVyeSA8LSAiU0VMRUNUICVzIEZST00gJXMuJXMiCiAgICBnZW9tX2NvbF9hcyA8LSBzcHJpbnRmKCIsICVzIGFzIGdlb20iLGdlb21fY29sMikKICAgIGNvbHVtbnMgPC0gcGFzdGUwKGNvbHVtbnMsZ2VvbV9jb2xfYXMpCiAgICBjb21wbGV0ZSA8LSBwYXN0ZTAodGhlX3F1ZXJ5LCIgIixvcHRpb25zKQoKICAgIGluaXRpYWwgPC0gUlBvc3RncmVTUUw6OmRiU2VuZFF1ZXJ5KGNvbm5lY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByaW50Zihjb21wbGV0ZSxjb2x1bW5zLHNjaGVtYSx0aGVfdGFibGUpKSAlPiUKICAgIHJldHJpZXZlX3Jlc3VsdCgpCgogICAgbXVuX3NocCA9IFdLVDJTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUoaW5pdGlhbCwgZ2VvbSA9ICJnZW9tIiwgaWQgPSBjb2xfc2hhcGUpCiAgICAjIG11bl9zaHAgPSBzcDo6U3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lKGluaXRpYWwsIGdlb20gPSAiZ2VvbSIsIGlkID0gY29sX3NoYXBlKQogICAgbXVuX2RmIDwtIGZvcnRpZnkobXVuX3NocCwgcmVnaW9uID0gY29sX3NoYXBlKQogICAgbmFtZXMobXVuX2RmKVtuYW1lcyhtdW5fZGYpPT0iaWQiXSA8LSBjb2xfc2hhcGUKCiAgICByZXR1cm4obXVuX2RmKQp9Cgpsb2FkX2dlb20yIDwtIGZ1bmN0aW9uKGNvbm5lY3Rpb24sc2NoZW1hLHRoZV90YWJsZSxjb2x1bW5zPSJcIkNWRUdFT1wiLCBcIkNWRV9FTlRcIiwgXCJDVkVfTVVOXCIiLCBnZW9tX2NvbCwgY29sX3NoYXBlLCBvcHRpb25zPSIiKXsKICAgIGdlb21fY29sIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShnZW9tX2NvbCkpCiAgICBzY2hlbWEgICAgPC0gZGVwYXJzZShzdWJzdGl0dXRlKHNjaGVtYSkpCiAgICB0aGVfdGFibGUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKHRoZV90YWJsZSkpCiAgICBjb2xfc2hhcGUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGNvbF9zaGFwZSkpCiAgICBnZW9tX2NvbDIgPC0gcGFzdGUwKCJcIiIsZ2VvbV9jb2wsIlwiIikKCiAgICB0aGVfcXVlcnkgPC0gIlNFTEVDVCAlcyBGUk9NICVzLiVzIgogICAgZ2VvbV9jb2xfYXMgPC0gc3ByaW50ZigiLCAlcyBhcyBnZW9tIixnZW9tX2NvbDIpCiAgICBjb2x1bW5zIDwtIHBhc3RlMChjb2x1bW5zLGdlb21fY29sX2FzKQogICAgY29tcGxldGUgPC0gcGFzdGUwKHRoZV9xdWVyeSwiICIsb3B0aW9ucykKCiAgICBpbml0aWFsIDwtIFJQb3N0Z3JlU1FMOjpkYlNlbmRRdWVyeShjb25uZWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmludGYoY29tcGxldGUsY29sdW1ucyxzY2hlbWEsdGhlX3RhYmxlKSkgJT4lCiAgICByZXRyaWV2ZV9yZXN1bHQoKQoKICAgIHJldHVybihpbml0aWFsKQp9CgojJyBAdGl0bGUgZGlzY29uX2RiCiMnCiMnIEBkZXNjcmlwdGlvbiBUaGlzIGZ1bmN0aW9uIGRpc2Nvbm5lY3RzIGEgUG9zdGdyZVNRTAojJyBjb25uZWN0aW9uLgojJwojJyBAcGFyYW0gY29ubmVjdGlvbiBEQkkgY29ubmVjdGlvbi4gQSBjb25uZWN0aW9uIHRvIGEgZGF0YWJhc2UgbXVzdCBiZSBvcGVuIGFuZCBnaXZlbi4KIycKIycgQGV4YW1wbGVzIGRpc2Nvbl9kYihjb24pCiMnIEBleHBvcnQKZGlzY29uX2RiIDwtIGZ1bmN0aW9uKGNvbm5lY3Rpb24pewogICAgUlBvc3RncmVTUUw6OmRiRGlzY29ubmVjdChjb25uZWN0aW9uKQp9CmBgYAoKCkZpcnN0IHdlIGxvYWQgdGhlIFBMQU5FQSByZXN1bHRzLgpgYGB7cn0KIyBkaXNjb25fZGIoY29uKQpjb24gPC0gcGxhbl9jb25uZWN0KCkKCnF1ZXJ5IDwtIGxvYWRfdGFibGUoY29uLHB1YmxpYyxwbGFuZWEpCnBsYW5lYSA8LSBxdWVyeSAlPiUgcmV0cmlldmVfcmVzdWx0KCkKY2xlYXJfcmVzdWx0cyhjb24pCmBgYAoKV2UgbG9hZCB0aGUgZ2VvbWV0cmllcyBidXQgd2lsbCB1c2UgdGhlbSBsYXRlcgpgYGB7cn0KZ2VvbV9tdW5pIDwtIGxvYWRfZ2VvbShjb24scHVibGljLGdlb21fbXVuaSxnZW9tX2NvbD1XS1QsY29sX3NoYXBlPUNWRUdFTykKYGBgCgoKYGBge3J9CnRoZV9uYW1lcyA8LSBjb2xuYW1lcyhwbGFuZWEpCmxvZ3JvcyA8LSB0aGVfbmFtZXNbMTk6bGVuZ3RoKCh0aGVfbmFtZXMpKV0KcGxhbmVhW2xvZ3Jvc10gPC0gc2FwcGx5KHBsYW5lYVtsb2dyb3NdLGFzLm51bWVyaWMpCnBsYW5lYSRtdW5pY2lwaW8gPC0gcGxhbmVhJG11bmljaXBpbyAlPiUgaWNvbnYoZnJvbT0iVVRGLTgiLHRvPSJBU0NJSS8vVFJBTlNMSVQiKSAlPiUgdG91cHBlcigpCmBgYAoKCldlIG5lZWQgdG8gY2hhbmdlIHNvbWUgbXVuaWNpcGFsaXRpZXMgbmFtZXMgaW4gb3JkZXIgdG8gam9pbiB0aGlzIGRhdGFiYXNlIHdpdGggdGhlIENPTkFQTyBvbmUuClRoaXMgYXJlIHRoZSBtaXNzZWQtbWF0Y2hlZCBuYW1lcyBpbiB0aGUgdGFibGVzIG9mIHRoZSBkYXRhYmFzZS4gSSdsbCBjaGFuZ2UgdGhlIG9uZXMgZnJvbSB0aGUgc2Nob29scy4KYGBge3J9CmZyb21fc2Nob29scyA8LSBjKCJDRC4gREVMIENBUk1FTiIsIkRPQ1RPUiBCRUxJU0FSSU8gRE9NSU5HVUVaIiwiVEVNT1NBQ0hJIiwiUEVKUFlPTiBCTEFOQ08iLCJET0xPUkVTIEhJREFMR08iLCJTSUxBTyIsIlpBUE9UTEFOIEVMIEdSQU5ERSAoQ0lVREFEIEdVWk1BTikiLCJDVUFVVElUTEFOIChERSBHQVJDSUEgQkFSUkFHQU4pIiwiU0FOIE1BUlRJTiBERSBCT0xBJk9TIiwiQ0EmQURBUyBERSBPQlJFR09OIiwiU0FOIEpPU0U/IERFTCBSSU5DT04iLCJWQUxMRSBERSBDSEFMQ08gU09MSURBUklEQSIsIlNBTiBNQVJUSU4gREUgTEFTIFBJUkFNSURFIiwiSEVST0lDQSBaSVRBQ1VBUk8iLCJUQU5IVUFUTyBERSBHVUVSUkVSTyIsIkFSSU8gREUgUk9TQUxFUyIsIlRVTUJJU0NBVElPIERFIFJVSVoiLCJKVU5HQVBFTyBERSBKVUFSRVoiLCJBQ1VJVFpJTyBERUwgQ0FOSkUiLCJTVVNVUFVBVE8gREUgR1VFUlJFUk8iLCJHQUJSSUVMIFpBTU9SQSAoTE9NQkFSRElBKSIsIkJFTklUTyBKVUFSRVogKExBVVJFTEVTKSIsIkhVRVRBTU8gREUgTlVORVoiLCJUSVFVSUNIRU8iLCJUTEFMUFVKQUhVQSBERSBSQVlPTiIsIlBBUkFDSE8gREUgVkVSRFVaQ08iLCJEUi4gQkVTTElTQVJJTyBET01JTkdVRVoiLCJUTEFMVElaQVBBTiIsIkVMIE5BWUFSIiwiU0FOIE1BVEVPIFlVQ1VUSU5ETyIsIkVMIFJPU0FSSU8iLCJBVExUWkFZQU5DQSIsIk1FREVMTElOIERFIEJSQVZPIiwiTkFOQ0hJVEFMIERFIExBWkFSTyBDQVJERU5BUyBERUwgUkkiKQoKdG9fY29uYXBvIDwtIGMoIkNBUk1FTiIsIkRSLiBCRUxJU0FSSU8gRE9NSU5HVUVaIiwiVEVNT1NBQ0hJQyIsIlBFTk9OIEJMQU5DTyIsIkRPTE9SRVMgSElEQUxHTyBDVU5BIERFIExBIElOREVQRU5ERU5DSUEgTkFDSU9OQUwiLCJTSUxBTyBERSBMQSBWSUNUT1JJQSIsIlpBUE9UTEFOIEVMIEdSQU5ERSIsIkNVQVVUSVRMQU4gREUgR0FSQ0lBIEJBUlJBR0FOIiwiU0FOIE1BUlRJTiBERSBCT0xBTk9TIiwiQ0FOQURBUyBERSBPQlJFR09OIiwiU0FOIEpPU0UgREVMIFJJTkNPTiIsIlZBTExFIERFIENIQUxDTyBTT0xJREFSSURBRCIsIlNBTiBNQVJUSU4gREUgTEFTIFBJUkFNSURFUyIsIlpJVEFDVUFSTyIsIlRBTkhVQVRPIiwiQVJJTyIsIlRVTUJJU0NBVElPIiwiSlVOR0FQRU8iLCJBQ1VJVFpJTyIsIlNVU1VQVUFUTyIsIkdBQlJJRUwgWkFNT1JBIiwiSlVBUkVaIiwiSFVFVEFNTyIsIlRJUVVJQ0hFTyBERSBOSUNPTEFTIFJPTUVSTyIsIlRMQUxQVUpBSFVBIiwiUEFSQUNITyIsIkRSLiBCRUxJU0FSSU8gRE9NSU5HVUVaIiwiVExBTFRJWkFQQU4gREUgWkFQQVRBIiwiREVMIE5BWUFSIiwiU0FOIE1BVEVPIFlVQ1VUSU5ET08iLCJST1NBUklPIiwiQUxUWkFZQU5DQSIsIk1FREVMTElOIiwiTkFOQ0hJVEFMIERFIExBWkFSTyBDQVJERU5BUyBERUwgUklPIikKCnBsYW5lYSRtdW5pY2lwaW8gPC0gcGx5cjo6bWFwdmFsdWVzKHBsYW5lYSRtdW5pY2lwaW8sIHRvPXRvX2NvbmFwbywgZnJvbT1mcm9tX3NjaG9vbHMpCmBgYAoKTG9hZCB0aGUgQ09OQVBPIHRhYmxlCmBgYHtyfQp0aGVfb3B0aW9ucyA8LSAiV0hFUkUgXCJBTk9cIiA9IDIwMTUiCnF1ZXJ5IDwtIGxvYWRfcXVlcnkoY29uLHB1YmxpYyxjb25hcG8sb3B0aW9ucz10aGVfb3B0aW9ucykKY29uYXBvIDwtIHF1ZXJ5ICU+JSByZXRyaWV2ZV9yZXN1bHQoKQpjbGVhcl9yZXN1bHRzKGNvbikKY29uYXBvJENMQVZFIDwtIGNvbmFwbyRDTEFWRSAlPiUgc3RyX3BhZCh3aWR0aD01LHBhZD0iMCIpCmNvbmFwbyROT01fRU5UICU8PiUgc3RyX3JlcGxhY2VfYWxsKCJPYXhhY2EuKiIsIk9heGFjYSIpCmNvbmFwbyRNVU4gPC0gY29uYXBvJE1VTiAlPiUgaWNvbnYoZnJvbT0iVVRGLTgiLHRvPSJBU0NJSS8vVFJBTlNMSVQiKSAlPiUgdG91cHBlcigpICU+JSBzdHJfcmVwbGFjZV9hbGwoIlNBTiBQRURSTyBNSVhURVBFQy4qIiwiU0FOIFBFRFJPIE1JWFRFUEVDIikKY29uYXBvICU8PiUgc2VsZWN0KENMQVZFLE5PTV9FTlQsTVVOLFNFWE8sUE9CKSAlPD4lIGRwbHlyOjpncm91cF9ieShDTEFWRSxOT01fRU5ULE1VTikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoUE9QPXN1bShQT0IpKQpgYGAKCkxvYWQgdGhlIG1hcmdpbmFsaXphdGlvbiB0YWJsZSBhbmQgY2hhbmdlIHRoZSB2YXJpYWJsZSBuYW1lcyB0byB1c2FibGUgb25lcy4KYGBge3J9CnRoZV9vcHRpb25zIDwtICJXSEVSRSBcIllFQVJcIiA9IDIwMTUiCnF1ZXJ5IDwtIGxvYWRfcXVlcnkoY29uLHB1YmxpYyxtYXJnaW5hbGl6YXRpb24sb3B0aW9ucz10aGVfb3B0aW9ucykKbWFyZ2luYWNpb24gPC0gcXVlcnkgJT4lIHJldHJpZXZlX3Jlc3VsdCgpCmNsZWFyX3Jlc3VsdHMoY29uKQoKIyBUT0RPIGNoYW5nZSB0aGlzIHRvIFNRTCBxdWVyeQptYXJnaW5hY2lvbiAlPD4lIHNlbGVjdCgtQ1ZFX0VOVCwtRU5ULC1NVU4sLVZQKQpjb2xuYW1lcyhtYXJnaW5hY2lvbikgPC0gYygiQ0xBVkUiLCJwb3BfbWFyZyIsImlsbGl0ZXJhY3kiLCJlbGVtZW50YXJ5Iiwibm9fc2V3YWdlIiwibm9fZWxlY3RyaWNpdHkiLCJub193YXRlciIsIm92ZXJjcm93ZGluZyIsImRpcnRfZmxvb3IiLCJsZXNzXzVrIiwibGVzc18ybWlud2FnZSIsIm92c2QiLCJvdnNkc2UiLCJpbSIsImdtIiwiaW5kMGExMDAiLCJsdWdhciIsImx1Z2FyZXN0IiwiWUVBUiIpCgojIFRPRE8gc2V0IENMQVZFIGFzIFRFWFQgaW4gbWFyZ2luYWxpemF0aW9uIHNjaGVtYSBmb3IgU1FMIGluZ2VzdCBpbiBwaXBlbGluZQptYXJnaW5hY2lvbiRDTEFWRSA8LSBhcy5jaGFyYWN0ZXIobWFyZ2luYWNpb24kQ0xBVkUpICU+JSBzdHJfcGFkKHdpZHRoPTUscGFkPSIwIikKY29sMm51bSA8LSBjb2xuYW1lcyhtYXJnaW5hY2lvbiAlPiUgc2VsZWN0KC1DTEFWRSwtZ20pKQptYXJnaW5hY2lvbltjb2wybnVtXSA8LSBzYXBwbHkobWFyZ2luYWNpb25bY29sMm51bV0sYXMubnVtZXJpYykKCm1hcmdpbmFjaW9uIDwtIG1hcmdpbmFjaW9uWyFpcy5uYShtYXJnaW5hY2lvbiRnbSksXQpgYGAKCgpKb2luIHRoZSB0aHJlZSB0YWJsZXMuIEkgc2hvdWxkIHJ1biB0aGlzIGluIFBvc3RncmVzLiAqVE9ETyogd3JpdGUgU1FMIHN0YXRlbWVudC4KYGBge3J9CnNjaG9vbHMgPC0gY29uYXBvICU+JSByaWdodF9qb2luKHBsYW5lYSxieT1jKCJOT01fRU5UIj0iZW50aWRhZCIsIk1VTiI9Im11bmljaXBpbyIpKSAlPiUgbGVmdF9qb2luKG1hcmdpbmFjaW9uLCBieT0iQ0xBVkUiKSAlPiUgdW5ncm91cChOT01fRU5UKQpzY2hvb2xzJGdtIDwtIGZhY3RvcihzY2hvb2xzJGdtLGxldmVscz1jKCJNdXkgYmFqbyIsIkJham8iLCJNZWRpbyIsIkFsdG8iLCJNdXkgYWx0byIpKQpzY2hvb2xzJHRpcG9fZXNjdWVsYSA8LSBmYWN0b3Ioc2Nob29scyR0aXBvX2VzY3VlbGEsbGV2ZWxzPWMoIkdlbmVyYWwgUMO6YmxpY2EiLCJUw6ljbmljYSBQw7pibGljYSIsIlRlbGVzZWN1bmRhcmlhIiwiUHJpdmFkYSIsIkNvbXVuaXRhcmlhIikpCmBgYAoKVGhpcyByZXN1bHQgaXMgZm9yIGEgdGFibGUgaW4gdGhlIGRvY3VtZW50CmBgYHtyfQptYXJnaW5hY2lvbiAlPiUgZ3JvdXBfYnkoZ20pICU+JSBzdW1tYXJpc2UobigpKQpgYGAKCkhvdyBtYW55IHNjaG9vbHMKYGBge3J9Cmxlbmd0aCh1bmlxdWUocGxhbmVhJGNsYXZlKSkKc2Nob29scyAlPiUgZ3JvdXBfYnkoZ20pICU+JSBzdW1tYXJpc2UobigpKQpgYGAKCmBgYHtyfQpzY2hvb2xzWyFpcy5uYShzY2hvb2xzJElWX3BvcmNfbGVuKSAmICFpcy5uYShzY2hvb2xzJElWX3BvcmNfbWF0KSxdICU+JSAgc3VtbWFyaXNlKG1lYW4oSVZfcG9yY19sZW4pLG1lYW4oSVZfcG9yY19tYXQpKQpgYGAKClRoZSBudW1iZXIgb2Ygc2Nob29scyBpbiBhIG11bmljaXBhbGl0eS4KYGBge3J9CnNjaG9vbHNfYnlfbXVuIDwtIHBsYW5lYSAlPiUgZGlzdGluY3QoZW50LG11bmljaXBpbyxlc2N1ZWxhLG1hcmdpbmFjaW9uKSAlPiUgZ3JvdXBfYnkoZW50LG11bmljaXBpbykgJT4lIHN1bW1hcmlzZShudW1iZXI9bigpKQpudW1fc2Nob29scyA8LSBzY2hvb2xzX2J5X211biAlPiUgZ3JvdXBfYnkobnVtYmVyKSAlPiUgc3VtbWFyaXNlKG51bV9zY2hvb2wgPSBuKCkpCmBgYAoKYGBge3J9CnNjaG9vbHMkQ0xBVkUgJT4lIHVuaXF1ZSgpICU+JSBsZW5ndGgoKQpjb25hcG8kQ0xBVkUgJT4lIHVuaXF1ZSgpICU+JSBsZW5ndGgoKQpgYGAKCkZpdCBwb3J3ZXItbGF3IHRvIG51bWJlciBvZiBzY2hvb2xzCmBgYHtyfQpzY2hvb2xzX3Bvd2VyX2xhdyA8LSBsbShsb2cxMChudW1fc2Nob29sKSB+IGxvZzEwKG51bWJlciksbnVtX3NjaG9vbHMpCmBgYAoKCmBgYHtyfQpzZXFfc2Nob29scyA8LSBzZXEobWluKG51bV9zY2hvb2xzKSxtYXgobnVtX3NjaG9vbHMpKQphZGp1c3RlZF9wb3dlciA8LSAxMF5zY2hvb2xzX3Bvd2VyX2xhdyRjb2VmZmljaWVudHNbMV0qc2VxX3NjaG9vbHNec2Nob29sc19wb3dlcl9sYXckY29lZmZpY2llbnRzWzJdCnNjaG9vbHNfcG93ZXJfbGF3CmBgYAoKCmBgYHtyfQp0aGVfcF9sYXcgPC0gdGliYmxlKG51bWJlcj1zZXFfc2Nob29scyx2YWx1ZT1hZGp1c3RlZF9wb3dlcikKbnVtX3NjaG9vbHMgJTw+JSBsZWZ0X2pvaW4odGhlX3BfbGF3LGJ5PSJudW1iZXIiKQpgYGAKCgpgYGB7cn0KZ2dwbG90KG51bV9zY2hvb2xzKSsKICBnZW9tX2JhcihhZXMoeD1udW1iZXIseT1udW1fc2Nob29sKSxzdGF0ID0gImlkZW50aXR5IikrCiAgZ2VvbV9saW5lKGFlcyh4PW51bWJlcix5PXZhbHVlKSxjb2xvcj0icmVkIikrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IlNjaG9vbHMiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJNdW5pY2lwYWxpdGllcyIsbGltaXRzID0gYygwLDI1MCkpKwogIGdndGl0bGUoIk11bmljaXBhbGl0aWVzIGJ5IG51bWJlciBvZiBzY2hvb2xzIikrCiAgdGgKCiMgVGhpcyBwYXRoIHNob3VsZCBwb2ludCB0byBhIHBsYWNlIGluc2lkZSB0aGUgZG9ja2VyIGNvbnRhaW5lcgojIGdnc2F2ZSgiLi4vLi4vZmlncy9wb3dlcmxhd19zY2hvb2xfbXVuLnBuZyIsd2lkdGggPSAxNSxoZWlnaHQgPSAxMCx1bml0cyA9ICJjbSIpCmBgYAoKYGBge3J9CmdncGxvdChudW1fc2Nob29scykrCiAgZ2VvbV9wb2ludChhZXMoeD1sb2cxMChudW1iZXIpLHk9bG9nMTAobnVtX3NjaG9vbCkpKSsKICAjIGdlb21fc21vb3RoKGFlcyh4PWxvZzEwKG51bWJlcikseT1sb2cxMChudW1fc2Nob29sKSksbWV0aG9kID0gImdsbSIpKwogIGdlb21fbGluZShhZXMoeD1sb2cxMChudW1iZXIpLHk9bG9nMTAodmFsdWUpKSxjb2xvcj0icmVkIikrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IlNjaG9vbHMiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJNdW5pY2lwYWxpdGllcyIpKwogIGdndGl0bGUoIk11bmljaXBhbGl0aWVzIGJ5IG51bWJlciBvZiBzY2hvb2xzIikrCiAgdGgKCiMgZ2dzYXZlKCIuLi8uLi9maWdzL3Bvd2VybGF3X3NjaG9vbF9tdW5fbG9nLnBuZyIsd2lkdGggPSAxNSxoZWlnaHQgPSAxMCx1bml0cyA9ICJjbSIpCmBgYAoKCgoKCgpOdW1iZXIgb2YgZXZhbHVhdGVkIHN0dWRlbnRzIGluIGxhbmd1YWdlCmBgYHtyfQpzdW0oYXMubnVtZXJpYyhwbGFuZWEkZXZhbHVhZG9zX2xlbikpCmBgYAoKTnVtYmVyIG9mIGV2YWx1YXRlZCBzdHVkZW50cyBpbiBtYXRoZW1hdGljcwpgYGB7cn0Kc3VtKGFzLm51bWVyaWMocGxhbmVhJGV2YWx1YWRvc19tYXQpKQpgYGAKCgpUaGVyZSdzIGEgdmFyaWFibGUgY2FsbGVkIHJlcHJlc2VudGF0aXZpdHkgZm9yIGJvdGggbWF0aCBhbmQgbGFuZ3VhZ2UgYnV0IGl0J3MgdW5jbGVhciB0aGUgcGVyY2VudGFnZSBjdXQtb2ZmLCB0aGVuIEkgd2lsbCB1c2UgdGhlIHNjaG9vbHMgd2l0aCBhYm92ZSA1MCUgb2YgYm90aCB0ZXN0cwpgYGB7cn0Kc2Nob29sc1soc2Nob29scyRyZXByZV9sZW4gPT0gIk5PIikgfCAoc2Nob29scyRyZXByZV9tYXQgPT0gIk5PIiksXQpgYGAKCmBgYHtyfQpzY2hvb2xzWyhzY2hvb2xzJHBvcmNfbGVuID4gNTAgJiBzY2hvb2xzJHBvcmNfbWF0ID4gNTApLF0KYGBgCgpgYGB7cn0Kc2Nob29sc1shKHNjaG9vbHMkcG9yY19sZW4gPiA1MCAmIHNjaG9vbHMkcG9yY19tYXQgPiA1MCksXQpgYGAKCmBgYHtyfQpzY2hvb2xzWyhzY2hvb2xzJHJlcHJlX2xlbiA9PSAiU8ONIikgJiAoc2Nob29scyRyZXByZV9tYXQgPT0gIlPDjSIpLF0Kc2Nob29scwpgYGAKCgoKCmBgYHtyfQpzY2hvb2xzIDwtIHNjaG9vbHNbKHNjaG9vbHMkcG9yY19sZW4gPiA1MCAmIHNjaG9vbHMkcG9yY19tYXQgPiA1MCksXQptdW5fc2Nob29scyA8LSBzY2hvb2xzICU+JQogIHNlbGVjdChDTEFWRSxlc2N1ZWxhLFBPUCxtYXJnaW5hY2lvbixnbSxlbGVtZW50YXJ5LGlsbGl0ZXJhY3kpICU+JQogIHVuaXF1ZSgpICU+JSBkcGx5cjo6Z3JvdXBfYnkoQ0xBVkUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UobnVtX3NjaG9vbCA9IG4oKSxQT0I9dW5pcXVlKFBPUCksZWxlbWVudGFyeT11bmlxdWUoZWxlbWVudGFyeSksaWxsaXRlcmFjeT11bmlxdWUoaWxsaXRlcmFjeSksZ209dW5pcXVlKGdtKSkgJT4lCiAgbXV0YXRlKHNwYyA9IG51bV9zY2hvb2wvKFBPQi8xMDAwMDApKQoKYGBgCgpOdW1iZXIgb2YgZXZhbHVhdGVkIHN0dWRlbnRzIGluIGxhbmd1YWdlCmBgYHtyfQpzdW0oYXMubnVtZXJpYyhzY2hvb2xzJGV2YWx1YWRvc19sZW4pKQpgYGAKCk51bWJlciBvZiBldmFsdWF0ZWQgc3R1ZGVudHMgaW4gbWF0aGVtYXRpY3MKYGBge3J9CnN1bShhcy5udW1lcmljKHNjaG9vbHMkZXZhbHVhZG9zX21hdCkpCmBgYAoKYGBge3J9CnVuaXF1ZSgoc2Nob29scyRjbGF2ZSkpICU+JSBsZW5ndGgoKQpgYGAKCmBgYHtyfQp1bmlxdWUoKHNjaG9vbHMkQ0xBVkUpKSAlPiUgbGVuZ3RoKCkKYGBgCgoKCmBgYHtyfQptYXJfc2NvcmVzIDwtIHNjaG9vbHMgJT4lIHNlbGVjdChDTEFWRSxQT1AsaWxsaXRlcmFjeSxlbGVtZW50YXJ5LElfcG9yY19sZW4sSUlfcG9yY19sZW4sSUlJX3BvcmNfbGVuLElWX3BvcmNfbGVuLElfcG9yY19tYXQsSUlfcG9yY19tYXQsSUlJX3BvcmNfbWF0LElWX3BvcmNfbWF0KQpzY2hvb2xzX2wgPC0gc2Nob29scyAlPiUgZ3JvdXBfYnkoQ0xBVkUsIGNsYXZlLCBlc2N1ZWxhLCBnbSkgJT4lIHN1bW1hcmlzZShudW1fc2Nob29sPW4oKSxQT1A9dW5pcXVlKFBPUCksaWxsaXRlcmFjeT11bmlxdWUoaWxsaXRlcmFjeSksZWxlbWVudGFyeT11bmlxdWUoZWxlbWVudGFyeSksSV9sZW4gPSBtZWRpYW4oSV9wb3JjX2xlbiksSUlfbGVuID0gbWVkaWFuKElJX3BvcmNfbGVuKSxJSUlfbGVuID0gbWVkaWFuKElJSV9wb3JjX2xlbiksSVZfbGVuID0gbWVkaWFuKElWX3BvcmNfbGVuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElfbWF0ID0gbWVkaWFuKElfcG9yY19tYXQpLElJX21hdCA9IG1lZGlhbihJSV9wb3JjX21hdCksSUlJX21hdCA9IG1lZGlhbihJSUlfcG9yY19tYXQpLElWX21hdCA9IG1lZGlhbihJVl9wb3JjX21hdCkpCgptYXJfc2NvcmVzICU8PiUgZ3JvdXBfYnkoQ0xBVkUpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG51bV9zY2hvb2w9bigpLFBPUD11bmlxdWUoUE9QKSxpbGxpdGVyYWN5PXVuaXF1ZShpbGxpdGVyYWN5KSxlbGVtZW50YXJ5PXVuaXF1ZShlbGVtZW50YXJ5KSxJX2xlbiA9IG1lYW4oSV9wb3JjX2xlbiksSUlfbGVuID0gbWVhbihJSV9wb3JjX2xlbiksSUlJX2xlbiA9IG1lYW4oSUlJX3BvcmNfbGVuKSxJVl9sZW4gPSBtZWFuKElWX3BvcmNfbGVuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElfbWF0ID0gbWVhbihJX3BvcmNfbWF0KSxJSV9tYXQgPSBtZWFuKElJX3BvcmNfbWF0KSxJSUlfbWF0ID0gbWVhbihJSUlfcG9yY19tYXQpLElWX21hdCA9IG1lYW4oSVZfcG9yY19tYXQpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KG11bl9zY2hvb2xzKSsKICBnZW9tX2hpc3RvZ3JhbShhZXMoc3BjKSxiaW53aWR0aCA9IDEwLGZpbGw9IiM4NDgyNTYiKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT0iU2Nob29scyBwZXIgMTAwLDAwMCBwb3AiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0iTXVuaWNpcGFsaXRpZXMiKSsKICB0aApgYGAKCgoKVHJhbnNsYXRlIHNvbWUgdmFyaWFibGVzIHRvIGVuZ2xpc2guIFRoZXJlIGFyZSBzb21lIHdpdGggcGVjdWxpYXIgbmFtZSB0aGF0IHNob3VsZG4ndCBiZSB0cmFuc2xhdGVkLgpgYGB7cn0Kc2Nob29sc190aWR5IDwtIHNjaG9vbHMgJT4lIHNlbGVjdCh0aXBvX2VzY3VlbGEsZ20sSV9wb3JjX2xlbixJSV9wb3JjX2xlbixJSUlfcG9yY19sZW4sSVZfcG9yY19sZW4sSV9wb3JjX21hdCxJSV9wb3JjX21hdCxJSUlfcG9yY19tYXQsSVZfcG9yY19tYXQpCnNjaG9vbHNfdGlkeSAlPD4lIHBpdm90X2xvbmdlcihjKElfcG9yY19sZW4sSUlfcG9yY19sZW4sSUlJX3BvcmNfbGVuLElWX3BvcmNfbGVuLElfcG9yY19tYXQsSUlfcG9yY19tYXQsSUlJX3BvcmNfbWF0LElWX3BvcmNfbWF0KSkKCnVuaXF1ZShzY2hvb2xzJHRpcG9fZXNjdWVsYSkKZW5nbGlzaFR5cGUgPC0gYygiQ29tdW5pdGFyaWEiLCJQdWJsaWMgRy4iLCAiUHJpdmF0ZSIsICJQdWJsaWMgVC4iLCAiVGVsZXNlY3VuZGFyaWEiKQpzcGFuaXNoVHlwZSA8LSBjKCJDb211bml0YXJpYSIsIkdlbmVyYWwgUMO6YmxpY2EiLCAiUHJpdmFkYSIsICJUw6ljbmljYSBQw7pibGljYSIsICJUZWxlc2VjdW5kYXJpYSIpCgplbmdsaXNoTSA8LSBjKCJWZXJ5IExvdyIsIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKQpzcGFuaXNoTSA8LSBjKCJNdXkgYmFqbyIsIkJham8iLCAiTWVkaW8iLCAiQWx0byIsICJNdXkgYWx0byIpCgpyZXN1bHRzIDwtIGMoIkxhbmd1YWdlIEkiLCAiTGFuZ3VhZ2UgSUkiLCAiTGFuZ3VhZ2UgSUlJIiwgIkxhbmd1YWdlIElWIiwgIk1hdGggSSIsICJNYXRoIElJIiwgIk1hdGggSUlJIiwgIk1hdGggSVYiKQpyZXN1bHRhZG9zIDwtIGMoIklfcG9yY19sZW4iLCJJSV9wb3JjX2xlbiIsIklJSV9wb3JjX2xlbiIsIklWX3BvcmNfbGVuIiwiSV9wb3JjX21hdCIsIklJX3BvcmNfbWF0IiwiSUlJX3BvcmNfbWF0IiwiSVZfcG9yY19tYXQiKQoKc2Nob29sc190aWR5JHRpcG9fZXNjdWVsYSA8LSBwbHlyOjptYXB2YWx1ZXMoc2Nob29sc190aWR5JHRpcG9fZXNjdWVsYSwgdG89ZW5nbGlzaFR5cGUsIGZyb209c3BhbmlzaFR5cGUpCnNjaG9vbHNfdGlkeSRuYW1lIDwtIHBseXI6Om1hcHZhbHVlcyhzY2hvb2xzX3RpZHkkbmFtZSwgdG89cmVzdWx0cywgZnJvbT1yZXN1bHRhZG9zKQpzY2hvb2xzX3RpZHkkZ20gPC0gcGx5cjo6bWFwdmFsdWVzKHNjaG9vbHNfdGlkeSRnbSwgdG89ZW5nbGlzaE0sIGZyb209c3BhbmlzaE0pCgpzY2hvb2xzX2wkZ20gPC0gcGx5cjo6bWFwdmFsdWVzKHNjaG9vbHNfbCRnbSwgdG89ZW5nbGlzaE0sIGZyb209c3BhbmlzaE0pCgojIHNjaG9vbHNfdGlkeSR0aXBvX2VzY3VlbGEgPC0gZmFjdG9yKHNjaG9vbHNfdGlkeSR0aXBvX2VzY3VlbGEsbGV2ZWxzPWMoIkdlbmVyYWwgUMO6YmxpY2EiLCJUw6ljbmljYSBQw7pibGljYSIsIlRlbGVzZWN1bmRhdGlhIiwiUHJpdmFkYSIsIkNvbXVuaXRhcmlhIikpCmBgYAoKCgpEZWZpbmUgdGhlIGNvbG9ycwpgYGB7cn0KYWx0b2MgPC0gIiNlM2IyM2MiCmJham9jIDwtICIjNzI1NzUyIgptZWRpb2MgPC0gIiNmZTY0YTMiCm1hbHRvYyA8LSAiIzc4YmM2MSIKbWJham9jIDwtICIjNGYzNTliIgpgYGAKCmBgYHtyfQptdCA8LSBnZ3Bsb3Qoc2Nob29sc190aWR5KSsKICBnZW9tX2JveHBsb3QoYWVzKHRpcG9fZXNjdWVsYSx2YWx1ZSxmaWxsPWdtKSxhbHBoYT0wLjgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiTWFyZ2luYWxpemF0aW9uIiwgdmFsdWVzID0gYygiSGlnaCIgPSBhbHRvYywgIkxvdyIgPSBiYWpvYywgIk1lZGl1bSIgPSBtZWRpb2MsICJWZXJ5IEhpZ2giID0gbWFsdG9jLCAiVmVyeSBMb3ciID0gbWJham9jKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lPSJTY2hvb2wgVHlwZSIpKyMsIGJyZWFrcyA9IGMoImEiLCJiIiwiYyIsImQiLCJlIikpKwogICMgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiUmlzayBBdmVyc2lvbiBNZWFzdXJlIiwgYnJlYWtzID0gYygwLDAuMiwwLjQsMC42LDAuOCwxKSwgbGltaXRzID0gYygtMC4xLDEuMSkpKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIlN0dWRlbnQgcGVyY2VudGFnZSIpKwogICMgdGhlbWVfY2xhc3NpYygpKwogIGdndGl0bGUoIlNjaG9vbC13aXNlIHN0dWRlbnQgcGVyY2VudGFnZSBieSBhY2hpZXZlbWVudCBsZXZlbCIpKwogIHRoKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgptdCArIGZhY2V0X2dyaWQodmFycyhuYW1lKSkrCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKCmdnc2F2ZSgiLi4vLi4vZmlncy9hY2hpZXZlbWVudF9sZXZlbHMucG5nIixoZWlnaHQgPSAyMCwgd2lkdGggPSA0MCwgdW5pdHMgPSAiY20iKQpgYGAKCkRlZmluZSBwbG90IGNvbG9ycwpgYGB7cn0KY29tYyA8LSAiI2VmZWNjYSIKcHJpdmMgPC0gIiNkYjJiMzkiCnB1YmdjIDwtICIjZjdmZjU4IgpwdWJ0YyA8LSAiI2ZmOTM0ZiIKdGVsZWMgPC0gIiMyOTMzNWMiCmBgYAoKYGBge3J9Cm10IDwtIGdncGxvdChzY2hvb2xzX3RpZHkpKwogIGdlb21fYm94cGxvdChhZXMoZ20sdmFsdWUsZmlsbD10aXBvX2VzY3VlbGEpLGFscGhhPTAuOCkrCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJTY2hvb2wgVHlwZSIsIHZhbHVlcyA9IGMoIkNvbXVuaXRhcmlhIiA9IGNvbWMsICJQcml2YXRlIiA9IHByaXZjLCAiUHVibGljIEcuIiA9IHB1YmdjLCAiUHVibGljIFQuIiA9IHB1YnRjLCAiVGVsZXNlY3VuZGFyaWEiID0gdGVsZWMpKSsKICAgICAgICAgICAgICAgICAgICAgICMgbGFiZWxzID0gYygiTG93IiwgIlZlcnkgTG93IiwgIk1lZGl1bSIsICJIaWdoIiwgIlZlcnkgSGlnaCIpKSsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWU9Ik1hcmdpbmFsaXphdGlvbiIpKyMsIGJyZWFrcyA9IGMoImEiLCJiIiwiYyIsImQiLCJlIikpKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIlN0dWRlbnQgcGVyY2VudGFnZSIpKwogIGdndGl0bGUoIlNjaG9vbC13aXNlIHN0dWRlbnQgcGVyY2VudGFnZSBieSBhY2hpZXZlbWVudCBsZXZlbCIpKwogIHRoKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgptdCArIGZhY2V0X2dyaWQodmFycyhuYW1lKSkgKwogICAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSIjYjUzYTMxIikpCgpnZ3NhdmUoIi4uLy4uL2ZpZ3MvYWNoaWV2ZW1lbnRfbGV2ZWxzX2J5X21hcmdpbmFsaXphdGlvbi5wbmciLGhlaWdodCA9IDIwLCB3aWR0aCA9IDQwLCB1bml0cyA9ICJjbSIpCmBgYAoKCgoKCmBgYHtyfQpzY2hvb2xzIDwtIGNvbmFwbyAlPiUgcmlnaHRfam9pbihwbGFuZWEsYnk9YygiTk9NX0VOVCI9ImVudGlkYWQiLCJNVU4iPSJtdW5pY2lwaW8iKSkgJT4lIGxlZnRfam9pbihtYXJnaW5hY2lvbiwgYnk9IkNMQVZFIikKdW5pcXVlKHNjaG9vbHMkdGlwb19lc2N1ZWxhKQpzY2hvb2xzJGdtIDwtIGZhY3RvcihzY2hvb2xzJGdtLGxldmVscz1jKCJNdXkgYmFqbyIsIkJham8iLCJNZWRpbyIsIkFsdG8iLCJNdXkgYWx0byIpKQpnZ3Bsb3Qoc2Nob29scykrCiAgZ2VvbV9ib3hwbG90KGFlcyh0aXBvX2VzY3VlbGEsSVZfcG9yY19tYXQsZmlsbD1nbSksYWxwaGE9MC42KSsKICB0aCsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQojIGdnc2F2ZSgiLi4vLi4vZmlncy9hY2hpZXZlbWVudF9sZXZlbHNfYnlfbWFyZ2luYWxpemF0aW9uLnBuZyIsaGVpZ2h0ID0gMjAsIHdpZHRoID0gNDAsIHVuaXRzID0gImNtIikKYGBgCgoKc2Nob29scyB3aXRoIDEwMCUgaW4gSVYgbGV2ZWwgZm9yIG1hdGhlbWF0aWNzCmBgYHtyfQp2YWwxMDAgPC0gbWFyX3Njb3Jlc1tvcmRlcihtYXJfc2NvcmVzJElWX21hdCxkZWNyZWFzaW5nID0gVCksXVsxOjQsXSRDTEFWRQpzY2hvb2xzW3NjaG9vbHMkQ0xBVkUgJWluJSB2YWwxMDAsXQpgYGAKCgpMb2FkIHRoZSBpbmRpZ2Vub3VzIHBvcHVsYXRpb24gYW5kIGpvaW50IGluZm9ybWF0aW9uIHdpdGggdGhlIHRlc3QuCmBgYHtyfQp0aGVfb3B0aW9ucyA8LSAiV0hFUkUgXCJNUE9cIiAhPSAnMDAwJyIKcXVlcnkgPC0gbG9hZF9xdWVyeShjb24scHVibGljLGluZGlnZW5vdXNfbGFuZ3VhZ2Usb3B0aW9ucyA9IHRoZV9vcHRpb25zKQppbmRpZyA8LSBxdWVyeSAlPiUgcmV0cmlldmVfcmVzdWx0KCkKY2xlYXJfcmVzdWx0cyhjb24pCgppbmRpZyAlPD4lIG11dGF0ZShDTEFWRT1wYXN0ZTAoRU5ULE1QTykscGhfYW5hbGYgPSBoQU5BTEZBL0lQT0JfSU5ESSwgcG1fYW5hbGYgPSBtQU5BTEZBL0lQT0JfSU5ESSkKbWFyX3Njb3JlcyAlPD4lIGxlZnRfam9pbihpbmRpZyxieT0iQ0xBVkUiKSAlPD4lIG11dGF0ZShwcm9wX2luZGkgPSBJUE9CX0lOREkvUE9QKQptYXJfc2NvcmVzICU8PiUgbGVmdF9qb2luKHNjaG9vbHMgJT4lIHNlbGVjdChDTEFWRSxnbSkpICU+JSB1bmlxdWUoKQptYXJfc2NvcmVzICU8PiUgbXV0YXRlKHBfbGVuID0gKElQaGxpL1BPUCkpCmBgYAoKCgpQbG90IHRoZSBwZXJjZW50YWdlIG9mIGluZGlnZW5vdXMgcG9wdWxhdGlvbiBieSBsZXZlbCBvZiBtYXJnaW5hbGl6YXRpb24uCmBgYHtyfQplbmdsaXNoVHlwZSA8LSBjKCJDb211bml0YXJpYSIsIlB1YmxpYyBHLiIsICJQcml2YXRlIiwgIlB1YmxpYyBULiIsICJUZWxlc2VjdW5kYXJpYSIpCnNwYW5pc2hUeXBlIDwtIGMoIkNvbXVuaXRhcmlhIiwiR2VuZXJhbCBQw7pibGljYSIsICJQcml2YWRhIiwgIlTDqWNuaWNhIFDDumJsaWNhIiwgIlRlbGVzZWN1bmRhcmlhIikKCm1hcl9zY29yZXMkZ20gPC0gcGx5cjo6bWFwdmFsdWVzKG1hcl9zY29yZXMkZ20sIHRvPWVuZ2xpc2hNLCBmcm9tPXNwYW5pc2hNKQoKbWFyX3Njb3JlcyA8LSBtYXJfc2NvcmVzWyFpcy5uYShtYXJfc2NvcmVzJGdtKSxdCgpnZ3Bsb3QobWFyX3Njb3JlcykrCiAgZ2VvbV9ib3hwbG90KGFlcyhnbSxwcm9wX2luZGksZmlsbD1nbSksYWxwaGE9MC44KSsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKwogIHNjYWxlX3hfZGlzY3JldGUobmFtZT0iTWFyZ2luYWxpemF0aW9uIikrIywgYnJlYWtzID0gYygiYSIsImIiLCJjIiwiZCIsImUiKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiUGVyY2VudGFnZSBvZiBpbmRpZ2Vub3VzIHBvcHVsYXRpb24iLGxpbWl0cyA9IGMoMCwxKSkrCiAgZ2d0aXRsZSgiSW5kaWdlbm91cyBwb3B1bGF0aW9uIGJ5IG1hcmdpbmFsaXphdGlvbiIpKwogICAgdGgKCmdnc2F2ZSgiLi4vLi4vZmlncy9pbmRpX3ZzX21hcmcucG5nIixoZWlnaHQgPSAyMCwgd2lkdGggPSA0MCwgdW5pdHMgPSAiY20iKQpgYGAKCiMjIFNvbWUgdmFyaWFibGVzIHRoYXQgd29uJ3QgbWFrZSBpdCBpbnRvIHRoZSBkb2N1bWVudAoKYGBge3J9CmdncGxvdChtYXJfc2NvcmVzKSsKICBnZW9tX3BvaW50KGFlcygocHJvcF9pbmRpKSwoaWxsaXRlcmFjeS8xMDApKSkrCiAgZ2VvbV9zbW9vdGgoYWVzKChwcm9wX2luZGkpLChpbGxpdGVyYWN5LzEwMCkpLG1ldGhvZCA9ICJsbSIsY29sb3I9InJlZCIpKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lPSJJbGxpdGVyYWN5IikrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IkluZGlnZW5vdXMgUG9wdWxhdGlvbiIpKwogIHRoCmBgYAoKQXMgZXhwZWN0ZWQgdGhlIHJhdGlvIG9mIGluZGlnZW5vdXMgcG9wdWxhdGlvbiBpbiBhIG11bmljaXBhbGl0eSBjb3JyZWxhdGVzIHBlcmZlY3RseSB3aXRoIHRoZSBwZXJjZW50YWdlIG9mIGluaGFiaXRhbnRzIHRoYXQgc3BlYWsgYSBpbmRpZ2Vub3VzIGxhbmd1YWdlLgpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKChwcm9wX2luZGkpLChwX2xlbikpKSsKICBnZW9tX3Ntb290aChhZXMoKHByb3BfaW5kaSksKHBfbGVuKSksbWV0aG9kID0gImxtIixjb2xvcj0icmVkIikrCiAgc2NhbGVfeV9sb2cxMChuYW1lPSJJbmRpZ2Vub3VzIExhbmd1YWdlIikrCiAgc2NhbGVfeF9sb2cxMChuYW1lPSJJbmRpZ2Vub3VzIFBvcHVsYXRpb24iKSsKICB0aApgYGAKClRoZSBtb3JlIGluZGlnZW5vdXMgbW9yZSBpbGxpdGVyYWN5LgoKYGBge3J9CmdncGxvdChtYXJfc2NvcmVzKSsKICBnZW9tX3BvaW50KGFlcygocF9sZW4pLChpbGxpdGVyYWN5LzEwMCkpKSsKICBnZW9tX3Ntb290aChhZXMoKHBfbGVuKSwoaWxsaXRlcmFjeS8xMDApKSxtZXRob2QgPSAibG0iLGNvbG9yPSJyZWQiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0iSWxsaXRlcmFjeSIpKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lPSJJbmRpZ2Vub3VzIExhbmd1YWdlIikrCiAgdGgKYGBgCgoKClRoZSBwZXJjZW50YWdlIG9mIGluZGlnZW5vdXMgcG9wdWxhdGlvbiBjb3JyZWxhdGVzIHBvc3NpdGV2aWx5IHdpdGggdGhlIHBlcmNlbnRhZ2Ugb2YgbGV2ZWwgMSBhY2hpZXZlbWVudC4KCmBgYHtyfQpsbShJX2xlbi8xMDAgfiBwX2xlbiwgbWFyX3Njb3JlcykKYGBgCgpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKChwX2xlbiksSV9sZW4vMTAwKSkrCiAgZ2VvbV9zbW9vdGgoYWVzKChwX2xlbiksSV9sZW4vMTAwKSxtZXRob2QgPSAibG0iKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICJJbmRpZ2Vub3VzIGxhbmd1YWdlIHNwZWFrZXJzIikrCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiTGFuZ3VhZ2UgSSBhY2hpZXZlbWVudCIpKwogIHRoCgpnZ3NhdmUoIi4uLy4uL2ZpZ3MvSUxhbl92c19pbmRpTGFuZy5wbmciLGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwLCB1bml0cyA9ICJjbSIpCmBgYAoKCldoaWxlIGZvciB0aGUgbGV2ZWwgSVYgaXQgaGFzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24KYGBge3J9CmxtKElWX2xlbi8xMDAgfiBwX2xlbiwgbWFyX3Njb3JlcykKYGBgCgpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKChwX2xlbiksSVZfbGVuLzEwMCkpKwogIGdlb21fc21vb3RoKGFlcygocF9sZW4pLElWX2xlbi8xMDApLG1ldGhvZCA9ICJsbSIpKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIkluZGlnZW5vdXMgbGFuZ3VhZ2Ugc3BlYWtlcnMiKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJMYW5ndWFnZSBJViBhY2hpZXZlbWVudCIpKwogIHRoCmdnc2F2ZSgiLi4vLi4vZmlncy9JVkxhbl92c19pbmRpTGFuZy5wbmciLGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwLCB1bml0cyA9ICJjbSIpCmBgYAoKCgoKYGBge3J9CiMgbWFyX3RpZHkgPC0gbWFyX3Njb3JlcyAlPiUgc2VsZWN0KHRpcG9fZXNjdWVsYSxnbSxJX3BvcmNfbGVuLElJX3BvcmNfbGVuLElJSV9wb3JjX2xlbixJVl9wb3JjX2xlbixJX3BvcmNfbWF0LElJX3BvcmNfbWF0LElJSV9wb3JjX21hdCxJVl9wb3JjX21hdCkKbWFyX3RpZHkgPC0gbWFyX3Njb3JlcyAlPiUgcGl2b3RfbG9uZ2VyKGMoSV9sZW4sSUlfbGVuLElJSV9sZW4sSVZfbGVuLElfbWF0LElJX21hdCxJSUlfbWF0LElWX21hdCkpICU+JSB1bmlxdWUoKQpgYGAKCgpIb3cgZG9lcyBsYW5ndWFnZSBhbmQgbWF0aCBsZXZlbCBJIGNvcnJlbGF0ZSBpbiBhIGhpZ2hseSBtYXJnaW5hbGl6ZWQgbXVuaWNpcGFsaXR5PwpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXNbbWFyX3Njb3JlcyRnbSA9PSAiVmVyeSBIaWdoIixdKSsKICBjb29yZF9maXhlZCgpKwogIGdlb21fYWJsaW5lKCkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxMDApKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEwMCkpKwogIGdlb21fcG9pbnQoYWVzKElfbGVuLElfbWF0KSkrCiAgdGgKYGBgCgoKd2hhdCBhYm91dCBhIHZlcnkgbG93IG1hcmdpbmFsaXplZCBhcmVhPwpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXNbbWFyX3Njb3JlcyRnbSA9PSAiVmVyeSBMb3ciLF0pKwogIGNvb3JkX2ZpeGVkKCkrCiAgZ2VvbV9hYmxpbmUoKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEwMCkpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMTAwKSkrCiAgZ2VvbV9wb2ludChhZXMoSV9sZW4sSV9tYXQpKSsKICB0aApgYGAKCmBgYHtyfQptdCA8LSBnZ3Bsb3QobWFyX3Njb3JlcykrCiAgZ2VvbV9wb2ludChhZXMobG9nMTAoSV9sZW4pLGxvZzEwKElfbWF0KSxjb2xvcj1nbSkpKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKwogIGNvb3JkX2ZpeGVkKCkrCiAgZ2VvbV9hYmxpbmUoKSsKICBzY2FsZV94X2xvZzEwKG5hbWUgPSAicGVyY2VudGFnZSBsYW5ndWFnZSIpKyMsbGltaXRzID0gYygwLDEwMCkpKwogIHNjYWxlX3lfbG9nMTAobmFtZSA9ICJwZXJjZW50YWdlIG1hdGhlbWF0aWNzIikrIyxsaW1pdHMgPSBjKDAsMTAwKSkrCiAgZ2d0aXRsZSgiQWNoaWV2ZW1lbnQgTGV2ZWwgSSBieSBtYXJnaW5hbGl6YXRpb24iKSsKICB0aCsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbXQgKyBmYWNldF9ncmlkKGNvbHM9dmFycyhnbSkpICsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQoKZ2dzYXZlKCIuLi8uLi9maWdzL21hdGhfdnNfbGFuZ3VhZ2VfSV9sb2cucG5nIixoZWlnaHQgPSAyMCwgd2lkdGggPSA0MCwgdW5pdHMgPSAiY20iKQpgYGAKCmBgYHtyfQpsbSgoSV9tYXQpIH4gKElfbGVuKSxtYXJfc2NvcmVzW21hcl9zY29yZXMkZ20gPT0gIlZlcnkgTG93IixdKQpsbSgoSV9tYXQpIH4gKElfbGVuKSxtYXJfc2NvcmVzW21hcl9zY29yZXMkZ20gPT0gIlZlcnkgSGlnaCIsXSkKYGBgCgpgYGB7cn0KZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKGxvZzEwKElWX2xlbiksbG9nMTAoSVZfbWF0KSkpKwogIGNvb3JkX2ZpeGVkKCkrCiAgZ2VvbV9hYmxpbmUoKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICJwZXJjZW50YWdlIGxhbmd1YWdlIikrIyxsaW1pdHMgPSBjKDAsMTAwKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAicGVyY2VudGFnZSBtYXRoZW1hdGljcyIpKyMsbGltaXRzID0gYygwLDEwMCkpKwogIGdndGl0bGUoIkFjaGlldmVtZW50IExldmVsIElWIikrCiAgdGgrCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgpnZ3NhdmUoIi4uLy4uL2ZpZ3MvbWF0aF92c19sYW5ndWFnZV9WSV9hbGxfbG9nLnBuZyIsaGVpZ2h0ID0gMjAsIHdpZHRoID0gNDAsIHVuaXRzID0gImNtIikKIyBnZ3NhdmUoIn4vRG9jdW1lbnRzL1VWTS9jb3Vyc2VzL0RTMi9hc3NpZ25tZW50cy9IVzgvbWF0aF92c19sYW5ndWFnZV9JVl9hbGwucG5nIixoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCwgdW5pdHMgPSAiY20iKQpgYGAKCgpgYGB7cn0KbXQgPC0gZ2dwbG90KHNjaG9vbHNfbCkrCiAgZ2VvbV9wb2ludChhZXMoKElWX2xlbiksKElWX21hdCksY29sb3I9Z20pKSsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJNYXJnaW5hbGl6YXRpb24iLCB2YWx1ZXMgPSBjKCJIaWdoIiA9IGFsdG9jLCAiTG93IiA9IGJham9jLCAiTWVkaXVtIiA9IG1lZGlvYywgIlZlcnkgSGlnaCIgPSBtYWx0b2MsICJWZXJ5IExvdyIgPSBtYmFqb2MpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiVmVyeSBMb3ciLCAiTG93IiwgIk1lZGl1bSIsICJIaWdoIiwgIlZlcnkgSGlnaCIpKSsKICBjb29yZF9maXhlZCgpKwogIGdlb21fYWJsaW5lKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAicGVyY2VudGFnZSBsYW5ndWFnZSIpKyMsbGltaXRzID0gYygwLDEwMCkpKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbWF0aGVtYXRpY3MiKSsjLGxpbWl0cyA9IGMoMCwxMDApKSsKICBnZ3RpdGxlKCJBY2hpZXZlbWVudCBMZXZlbCBJViBieSBtYXJnaW5hbGl6YXRpb24iKSsKICB0aCsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbXQgKyBmYWNldF9ncmlkKGNvbHM9dmFycyhnbSkpICsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQoKYGBgCgoKYGBge3J9CnNjaG9vbHNfbCAlPD4lIG11dGF0ZShkaWZmX0kgPSBJX2xlbi1JX21hdCwgZGlmZl9JViA9IElWX2xlbi1JVl9tYXQpCgptdCA8LSBnZ3Bsb3Qoc2Nob29sc19sKSsKICBnZW9tX3BvaW50KGFlcygoZGlmZl9JViksKGRpZmZfSSksY29sb3I9Z20pKSsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJNYXJnaW5hbGl6YXRpb24iLCB2YWx1ZXMgPSBjKCJIaWdoIiA9IGFsdG9jLCAiTG93IiA9IGJham9jLCAiTWVkaXVtIiA9IG1lZGlvYywgIlZlcnkgSGlnaCIgPSBtYWx0b2MsICJWZXJ5IExvdyIgPSBtYmFqb2MpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiVmVyeSBMb3ciLCAiTG93IiwgIk1lZGl1bSIsICJIaWdoIiwgIlZlcnkgSGlnaCIpKSsKICBjb29yZF9maXhlZCgpKwogIGdlb21fYWJsaW5lKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAicGVyY2VudGFnZSBsYW5ndWFnZSIpKyMsbGltaXRzID0gYygwLDEwMCkpKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbWF0aGVtYXRpY3MiKSsjLGxpbWl0cyA9IGMoMCwxMDApKSsKICBnZ3RpdGxlKCJBY2hpZXZlbWVudCBMZXZlbCBJViBieSBtYXJnaW5hbGl6YXRpb24iKSsKICB0aCsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbXQgKyBmYWNldF9ncmlkKGNvbHM9dmFycyhnbSkpICsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQpgYGAKCgpgYGB7cn0KbXQgPC0gZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKGxvZzEwKElWX2xlbiksbG9nMTAoSVZfbWF0KSxjb2xvcj1nbSkpKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKwogIGNvb3JkX2ZpeGVkKCkrCiAgZ2VvbV9hYmxpbmUoKSsKICBzY2FsZV94X2xvZzEwKG5hbWUgPSAicGVyY2VudGFnZSBsYW5ndWFnZSIpKyMsbGltaXRzID0gYygwLDEwMCkpKwogIHNjYWxlX3lfbG9nMTAobmFtZSA9ICJwZXJjZW50YWdlIG1hdGhlbWF0aWNzIikrIyxsaW1pdHMgPSBjKDAsMTAwKSkrCiAgZ2d0aXRsZSgiQWNoaWV2ZW1lbnQgTGV2ZWwgSVYgYnkgbWFyZ2luYWxpemF0aW9uIikrCiAgdGgrCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCm10ICsgZmFjZXRfZ3JpZChjb2xzPXZhcnMoZ20pKSArCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKCmdnc2F2ZSgiLi4vLi4vZmlncy9tYXRoX3ZzX2xhbmd1YWdlX0lWX2xvZy5wbmciLGhlaWdodCA9IDIwLCB3aWR0aCA9IDQwLCB1bml0cyA9ICJjbSIpCmBgYAoKCgpgYGB7cn0KbXQgPC0gZ2dwbG90KG1hcl9zY29yZXMpKwogIGdlb21fcG9pbnQoYWVzKChJVl9sZW4pLChJVl9tYXQpLGNvbG9yPWdtKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiTWFyZ2luYWxpemF0aW9uIiwgdmFsdWVzID0gYygiSGlnaCIgPSBhbHRvYywgIkxvdyIgPSBiYWpvYywgIk1lZGl1bSIgPSBtZWRpb2MsICJWZXJ5IEhpZ2giID0gbWFsdG9jLCAiVmVyeSBMb3ciID0gbWJham9jKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgY29vcmRfZml4ZWQoKSsKICBnZW9tX2FibGluZSgpKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbGFuZ3VhZ2UiKSsjLGxpbWl0cyA9IGMoMCwxMDApKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJwZXJjZW50YWdlIG1hdGhlbWF0aWNzIikrIyxsaW1pdHMgPSBjKDAsMTAwKSkrCiAgZ2d0aXRsZSgiQWNoaWV2ZW1lbnQgTGV2ZWwgSVYgYnkgbWFyZ2luYWxpemF0aW9uIikrCiAgdGgrCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCm10ICsgZmFjZXRfZ3JpZChjb2xzPXZhcnMoZ20pKSArCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKCmBgYAoKCgoKCmBgYHtyfQpsbSgoSVZfbWF0KSB+IChJVl9sZW4pLG1hcl9zY29yZXNbbWFyX3Njb3JlcyRnbSA9PSAiVmVyeSBMb3ciLF0pCmxtKChJVl9tYXQpIH4gKElWX2xlbiksbWFyX3Njb3Jlc1ttYXJfc2NvcmVzJGdtID09ICJWZXJ5IEhpZ2giLF0pCmBgYAoKCgoKYGBge3J9Cm10IDwtIGdncGxvdChtYXJfc2NvcmVzKSsKICBnZW9tX3BvaW50KGFlcygoSVZfbGVuKSwoSV9tYXQpLGNvbG9yPWdtKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiTWFyZ2luYWxpemF0aW9uIiwgdmFsdWVzID0gYygiSGlnaCIgPSBhbHRvYywgIkxvdyIgPSBiYWpvYywgIk1lZGl1bSIgPSBtZWRpb2MsICJWZXJ5IEhpZ2giID0gbWFsdG9jLCAiVmVyeSBMb3ciID0gbWJham9jKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgY29vcmRfZml4ZWQoKSsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAxMDAsIHNsb3BlPS0xKSsKICAjIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbGFuZ3VhZ2UiLGxpbWl0cyA9IGMoMCwxMDApKSsKICAjIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbWF0aGVtYXRpY3MiLGxpbWl0cyA9IGMoMCwxMDApKSsKICBnZ3RpdGxlKCJBY2hpZXZlbWVudCBMZXZlbCBJViBieSBtYXJnaW5hbGl6YXRpb24iKSsKICB0aCsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKbXQgKyBmYWNldF9ncmlkKGNvbHM9dmFycyhnbSkpICsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQoKIyBnZ3NhdmUoIn4vRG9jdW1lbnRzL1VWTS9jb3Vyc2VzL0RTMi9hc3NpZ25tZW50cy9IVzYvbWF0aF92c19sYW5ndWFnZV9JVi5wbmciLGhlaWdodCA9IDIwLCB3aWR0aCA9IDQwLCB1bml0cyA9ICJjbSIpCmBgYAoKYGBge3J9Cm10IDwtIGdncGxvdChtYXJfc2NvcmVzKSsKICBnZW9tX3BvaW50KGFlcygocHJvcF9pbmRpKSwoSVZfbGVuKSxjb2xvcj1nbSksYWxwaGE9MC42KSsKICAjIGdlb21fZGVuc2l0eShhZXMocG1fYW5hbGYpLCkrCiAgIyBnZW9tX3BvaW50KGFlcyhwaF9hbmFsZixwcm9wX2luZGksY29sb3I9Z20pLGFscGhhPTAuNCkrCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiTWFyZ2luYWxpemF0aW9uIiwgdmFsdWVzID0gYygiSGlnaCIgPSBhbHRvYywgIkxvdyIgPSBiYWpvYywgIk1lZGl1bSIgPSBtZWRpb2MsICJWZXJ5IEhpZ2giID0gbWFsdG9jLCAiVmVyeSBMb3ciID0gbWJham9jKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgIyBjb29yZF9maXhlZCgpKwogICMgZ2VvbV9hYmxpbmUoKSsKICAjIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gInBlcmNlbnRhZ2UgbGFuZ3VhZ2UiLGxpbWl0cyA9IGMoMCwxKSkrCiAgIyBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJwZXJjZW50YWdlIG1hdGhlbWF0aWNzIixsaW1pdHMgPSBjKDAsMTAwKSkrCiAgZ2d0aXRsZSgiQWNoaWV2ZW1lbnQgTGV2ZWwgSVYgYnkgbWFyZ2luYWxpemF0aW9uIikrCiAgdGgrCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCm10ICsgZmFjZXRfZ3JpZChjb2xzPXZhcnMoZ20pKSArCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKCiMgZ2dzYXZlKCJ+L0RvY3VtZW50cy9VVk0vY291cnNlcy9EUzIvYXNzaWdubWVudHMvSFc2L21hdGhfdnNfbGFuZ3VhZ2VfSVYucG5nIixoZWlnaHQgPSAyMCwgd2lkdGggPSA0MCwgdW5pdHMgPSAiY20iKQpgYGAKCiMgTWFwcyBhbmQgdGhpbmdzCgoKYGBge3IsZWNobz1GQUxTRSx3YXJuaW5nPUZBTFNFLGluY2x1ZGU9RkFMU0V9CnBhcXVldGluZXMgPC0gYygiZ2dwbG90MiIsInBsb3RseSIsInRpYmJsZSIsInRpZHlyIiwibWFncml0dHIiLCJkcGx5ciIsInJlYWRyIiwiZ2dwbWlzYyIsInNmIiwKICAgICAgICAgICAgICAgICJyZXNoYXBlMiIsInJhbmdlTWFwcGVyIiwiUkNvbG9yQnJld2VyIiwic3RyaW5nciIsInF1YWRtZXNoIiwicmdsIiwicHN5Y2giLCJwdXJyciIsCiAgICAgICAgICAgICAgICAicmdkYWwiLCJkZWxkaXIiLCJnZ3RoZW1lcyIsImJyb29tIiwibGVhZmxldCIsImh0bWx0b29scyIsInNwYXRzdGF0IiwiZ2Vvc3BoZXJlIikgIyJzZiIKbm9faW5zdGFsYWRvcyA8LSBwYXF1ZXRpbmVzWyEocGFxdWV0aW5lcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0KaWYobGVuZ3RoKG5vX2luc3RhbGFkb3MpKSBpbnN0YWxsLnBhY2thZ2VzKG5vX2luc3RhbGFkb3MpCmxhcHBseShwYXF1ZXRpbmVzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCmBgYAoKSSBkZWZpbmVkIGEgZnVuY3Rpb24gdG8gZ2V0IHRoZSBnZW9tZXRyaWVzIGluIGFuIGVhc3kgd2F5LiAqKlRPRE8qKjogdXBkYXRlIF9mb3J0aWZ5XyB3aXRoIF9icm9vbV8KCgpgYGB7cix3YXJuaW5nPUZBTFNFfQppbmRpZ19wb3AgPC0gaW5kaWcgJT4lIGxlZnRfam9pbihjb25hcG8sYnk9IkNMQVZFIikKaW5kaWdfcG9wICU8PiUgbXV0YXRlKHBfaW5kaWcgPSBJUE9CX0lOREkvUE9QKQptdW5pY2lwaW9zX3BvcCA8LSBnZW9tX211bmkgJT4lIGxlZnRfam9pbihpbmRpZ19wb3AsIGJ5ID0gYygiQ1ZFR0VPIj0iQ0xBVkUiKSkKZ2VvbV9tdW5pJENWRUdFTyAlPiUgdW5pcXVlKCkgJT4lIGxlbmd0aCgpCmBgYAoKV2UgaGF2ZSA3IG11bmljaXBhbGl0aWVzIHdpdGhvdXQgaW5kaWdlbm91cyBpbmZvcm1hdGlvbiwgSSBkb24ndCBrbm93IGlmIGl0IGlzIGJlY2F1c2UgdGhleSB3ZXJlIGluc3RpdHV0ZWQgYmV0d2VlbiAyMDE1IGFuZCAyMDE4LCB3aGljaCBpcyB0aGUgbGFzdCBDT05BUE8gYXZhaWxhYmxlIGRhdGEgYW5kIHRoZSBnZW9tZXRyeSBkYXRhIHRoYXQgSSBoYXZlLiAoMjQ1NiB0byAyNDYzLCBhcyBpbiAyMDIwIHRoZXJlIGFyZSAyNDY0KQpgYGB7cix3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoKSsKICBnZW9tX3BvbHlnb24oZGF0YSA9IG11bmljaXBpb3NfcG9wLAogICAgICAgICAgICAgICBhZXMobG9uZyxsYXQsbGFiZWw9Q1ZFR0VPLGdyb3VwPWdyb3VwLAogICAgICAgICAgICAgICAgICAgZmlsbD1wX2luZGlnKjEwMCksY29sb3I9ImdyZXkiLHNpemU9MC4xKSsKICBjb29yZF9tYXAocHJvamVjdGlvbiA9ICJtZXJjYXRvciIpKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhuYW1lPSJQZXJjZW50YWdlIixsb3c9IndoaXRlIiwgaGlnaD0iIzBmNWE1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9ImNvbG9yYmFyIixsaW1pdHM9YygwLDEwMCkpKwogIGxhYnModGl0bGU9IkluZGlnZW5vdXMgUG9wdWxhdGlvbiIpKwogIHRoZW1lKCNwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheSIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyYXkyMCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3Q9MCksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIsaGp1c3Q9MC4zOSkpCgpnZ3NhdmUoIi4uLy4uL2ZpZ3MvbWFwX2luZGlnZW5vdXMucG5nIixoZWlnaHQgPSAzMCwgd2lkdGggPSAzMCwgdW5pdHMgPSAiY20iKQpgYGAKCgpgYGB7cix3YXJuaW5nPUZBTFNFfQptdW5pY2lwaW9zX25hbCA8LSBnZW9tX211bmkgJT4lIGxlZnRfam9pbihtYXJfc2NvcmVzLCBieSA9IGMoIkNWRUdFTyI9IkNMQVZFIikpCmBgYAoKCkEgdGlsdCBpbiB0aGUgbWFwIGFsbG93cyBhIGJldHRlciBzdGFja2luZwpgYGB7cixmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTZ9CiMgU2hlYXIvc2NhbGUgbWF0cml4IFtbMiwxXSxbMCwxXV0gb2J0YWluZWQgYnkgc29tZSB0cmlhbCBhbmQgZXJyb3I6CnNtIDwtIG1hdHJpeChjKDEsLTAuMywwLDEpLDIsMikKIyBHZXQgdHJhbnNmb3JtZWQgY29vcmRpbmF0ZXM6Cnh5IDwtIGFzLm1hdHJpeChtdW5pY2lwaW9zX25hbFssYygibG9uZyIsImxhdCIpXSkgJSolIHNtCiMgQWRkIHh5IGFzIGV4dHJhIGNvbHVtbnMgaW4gZm9ydGlmaWVkIGRhdGE6Cm11bmljaXBpb3NfbmFsJHggPC0geHlbLDFdOyBtdW5pY2lwaW9zX25hbCR5ID0geHlbLDJdCmBgYAoKYGBge3J9Cm1pbihtdW5pY2lwaW9zX25hbCR4KQptYXgobXVuaWNpcGlvc19uYWwkeCkKCm1heChtdW5pY2lwaW9zX25hbCR5KSAtIG1pbihtdW5pY2lwaW9zX25hbCR5KQptaW4obXVuaWNpcGlvc19uYWwkeSkKbWVhbihtdW5pY2lwaW9zX25hbCR5KQpgYGAKClN0YWNrZWQgbWFwIGZvciB0aGUgNCBsZXZlbHMgb2YgbWF0aApgYGB7cixmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTZ9CnAgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbihkYXRhID0gbXVuaWNpcGlvc19uYWwsIGFlcyh4LCB5LGxhYmVsPUNWRUdFTywgZ3JvdXA9Z3JvdXAsZmlsbD0oSV9tYXQpKSxjb2xvcj0id2hpdGUiLHNpemU9MC4wMSkrCiAgYW5ub3RhdGUoInRleHQiLHg9bWluKG11bmljaXBpb3NfbmFsJHgpKzEwLHk9bWVhbihtdW5pY2lwaW9zX25hbCR5KSxjb2xvcj0iZ3JheTM1IixsYWJlbD0iTGV2ZWwgSSIpKwogIGdlb21fcG9seWdvbihkYXRhID0gbXVuaWNpcGlvc19uYWwsIGFlcyh4LCB5LTE0LGxhYmVsPUNWRUdFTywgZ3JvdXA9Z3JvdXAsZmlsbD0oSUlfbWF0KSksY29sb3I9IndoaXRlIixzaXplPTAuMDEpKwogIGFubm90YXRlKCJ0ZXh0Iix4PW1pbihtdW5pY2lwaW9zX25hbCR4KSsxMCx5PW1lYW4obXVuaWNpcGlvc19uYWwkeS0xNCksY29sb3I9ImdyYXkzNSIsbGFiZWw9IkxldmVsIElJIikrCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBtdW5pY2lwaW9zX25hbCwgYWVzKHgsIHktMjgsbGFiZWw9Q1ZFR0VPLCBncm91cD1ncm91cCxmaWxsPShJSUlfbWF0KSksY29sb3I9IndoaXRlIixzaXplPTAuMDEpKwogIGFubm90YXRlKCJ0ZXh0Iix4PW1pbihtdW5pY2lwaW9zX25hbCR4KSsxMCx5PW1lYW4obXVuaWNpcGlvc19uYWwkeS0yOCksY29sb3I9ImdyYXkzNSIsbGFiZWw9IkxldmVsIElJSSIpKwogIGdlb21fcG9seWdvbihkYXRhID0gbXVuaWNpcGlvc19uYWwsIGFlcyh4LCB5LTQyLGxhYmVsPUNWRUdFTywgZ3JvdXA9Z3JvdXAsZmlsbD0oSVZfbWF0KSksY29sb3I9IndoaXRlIixzaXplPTAuMDEpKwogIGFubm90YXRlKCJ0ZXh0Iix4PW1pbihtdW5pY2lwaW9zX25hbCR4KSsxMCx5PW1lYW4obXVuaWNpcGlvc19uYWwkeS00MiksY29sb3I9ImdyYXkzNSIsbGFiZWw9IkxldmVsIElWIikrCiAgbGFicyh0aXRsZT0iTWF0aGVtYXRpY3MiKSsKICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobmFtZSA9ICJNZWFuIHBlcmNlbnRhZ2UiLGxvdz0id2hpdGUiLCBoaWdoPSIjNUEwRDM1IiwgZ3VpZGU9ImNvbG9yYmFyIikrIyxuYS52YWx1ZT0iI2YyZjJmMiIpKwogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gIm1lcmNhdG9yIikrCiAgdGhlbWUoI3BhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5IiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JheTIwIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgIyBsZWdlbmQuYm94ID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0PTApLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLGhqdXN0PTAuMzkpKQoKZ2dzYXZlKCIuLi8uLi9maWdzL3N0YWNrZWRfbWF0aC5wbmciLHAsaGVpZ2h0ID0gMzAsIHdpZHRoID0gMzAsIHVuaXRzID0gImNtIikKYGBgCgphbmQgbGFuZ3VhZ2UKYGBge3IsZmlnLndpZHRoPTYsZmlnLmhlaWdodD02fQpwIDwtIGdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IG11bmljaXBpb3NfbmFsLCBhZXMoeCwgeSxsYWJlbD1DVkVHRU8sIGdyb3VwPWdyb3VwLGZpbGw9KElfbGVuKSksY29sb3I9IndoaXRlIixzaXplPTAuMDEpKwogIGFubm90YXRlKCJ0ZXh0Iix4PW1pbihtdW5pY2lwaW9zX25hbCR4KSsxMCx5PW1lYW4obXVuaWNpcGlvc19uYWwkeSksY29sb3I9ImdyYXkzNSIsbGFiZWw9IkxldmVsIEkiKSsKICBnZW9tX3BvbHlnb24oZGF0YSA9IG11bmljaXBpb3NfbmFsLCBhZXMoeCwgeS0xNCxsYWJlbD1DVkVHRU8sIGdyb3VwPWdyb3VwLGZpbGw9KElJX2xlbikpLGNvbG9yPSJ3aGl0ZSIsc2l6ZT0wLjAxKSsKICBhbm5vdGF0ZSgidGV4dCIseD1taW4obXVuaWNpcGlvc19uYWwkeCkrMTAseT1tZWFuKG11bmljaXBpb3NfbmFsJHktMTQpLGNvbG9yPSJncmF5MzUiLGxhYmVsPSJMZXZlbCBJSSIpKwogIGdlb21fcG9seWdvbihkYXRhID0gbXVuaWNpcGlvc19uYWwsIGFlcyh4LCB5LTI4LGxhYmVsPUNWRUdFTywgZ3JvdXA9Z3JvdXAsZmlsbD0oSUlJX2xlbikpLGNvbG9yPSJ3aGl0ZSIsc2l6ZT0wLjAxKSsKICBhbm5vdGF0ZSgidGV4dCIseD1taW4obXVuaWNpcGlvc19uYWwkeCkrMTAseT1tZWFuKG11bmljaXBpb3NfbmFsJHktMjgpLGNvbG9yPSJncmF5MzUiLGxhYmVsPSJMZXZlbCBJSUkiKSsKICBnZW9tX3BvbHlnb24oZGF0YSA9IG11bmljaXBpb3NfbmFsLCBhZXMoeCwgeS00MixsYWJlbD1DVkVHRU8sIGdyb3VwPWdyb3VwLGZpbGw9KElWX2xlbikpLGNvbG9yPSJ3aGl0ZSIsc2l6ZT0wLjAxKSsKICBhbm5vdGF0ZSgidGV4dCIseD1taW4obXVuaWNpcGlvc19uYWwkeCkrMTAseT1tZWFuKG11bmljaXBpb3NfbmFsJHktNDIpLGNvbG9yPSJncmF5MzUiLGxhYmVsPSJMZXZlbCBJViIpKwogIGxhYnModGl0bGU9Ikxhbmd1YWdlIikrCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKG5hbWUgPSAiTWVhbiBwZXJjZW50YWdlIixsb3c9IndoaXRlIiwgaGlnaD0iIzVhNWEwZCIsIGd1aWRlPSJjb2xvcmJhciIpKyMsbmEudmFsdWU9IiNmMmYyZjIiKSsKICBjb29yZF9tYXAocHJvamVjdGlvbiA9ICJtZXJjYXRvciIpKwogIHRoZW1lKCNwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheSIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyYXkyMCIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICMgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdD0wKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIixoanVzdD0wLjM5KSkKCmdnc2F2ZSgiLi4vLi4vZmlncy9zdGFja2VkX2xhbmd1YWdlLnBuZyIscCxoZWlnaHQgPSAzMCwgd2lkdGggPSAzMCwgdW5pdHMgPSAiY20iKQpgYGAKCkkgd2FudCB0byBjb21wdXRlIHRoZSBkZW5zaXR5IG9mIHNjaG9vbHMgYnkgc3F1YXJlIGtpbG9tZXRlcgpgYGB7cn0KcXVlcnkgPC0gbG9hZF90YWJsZShjb24scHVibGljLGdlb21fbXVuaSkKbXVuaV9kZiA8LSBxdWVyeSAlPiUgcmV0cmlldmVfcmVzdWx0KCkKY2xlYXJfcmVzdWx0cyhjb24pCmBgYAoKQnVpbGQgdGhlIHBvbHlnb25zIGFuZCBjb21wdXRlIHRoZSBhcmVhIGluIEttXjIKYGBge3Isd2FybmluZz1GQUxTRX0KcG9sbWFuIDwtIGxhcHBseShtdW5pX2RmJFdLVCxyZ2Vvczo6cmVhZFdLVCkKcmdlb3M6OmdVbmlvbihwb2xtYW5bWzFdXSxwb2xtYW5bWzJdXSkKbGVuZ3RoKHBvbG1hbikKCnRoZV9hcmVhcyA8LSBzYXBwbHkocG9sbWFuLGdlb3NwaGVyZTo6YXJlYVBvbHlnb24pCmFyZWFzX2RmIDwtIHRpYmJsZShDVkVHRU89bXVuaV9kZiRDVkVHRU8sYXJlYT10aGVfYXJlYXMvMTAwMDAwMCkKYGBgCgpOb3cgd2UgbmVlZCB0aGUgbnVtYmVyIG9mIFNjaG9vbHMgYnV0IG5vdCBmcm9tIHRoZSB0ZXN0LCB3ZSdsbCB1c2UgdGhlIGRhdGEgZnJvbSBDb25zZWpvIE5hY2lvbmFsIGRlIEZvbWVudG8gRWR1Y2F0aXZvIChDT05BRkUpIHRocm91Z2ggdGhlIENlbnNvIGRlIEVzY3VlbGFzLCBNYWVzdHJvcyB5IEFsdW1ub3MgZGUgRWR1Y2FjacOzbiBCw6FzaWNhIHkgRXNwZWNpYWwgKENFTUFCRSkKTm9wLCBub3cgSSBub3cgdGhhdCBDT05BRkUgaXMgYSBwcm9ncmFtICB0byBjb250cmlidXRlIGNoaWxkcmVuIGluIGhpZ2hseSBtYXJnaW5hbGl6ZWQgYXJlYXMgdG8gY29uY2x1ZGUgdGhlaXIgYmFzaWMgY29tdW5pdGFyeSBlZHVjYXRpb24sIHdpdGggZWNvbm9taWNhbCBzdXBwb3J0IHRvIGVkdWNhdGlvbmFsIGZpZ3VyZXMgYW5kIHNjaG9sYXJseSBzdXBwbGllcyB0byBzdHVkZW50cy4KSXQgd291bGQgYmUgaW50ZXJlc3RpbmcgdG8gYW5hbGl6ZSB0aGlzIGludG8gZGVlcCEhIQoKVGhpcyBpcyB0aGUgbnVtYmVyIG9mIHNjaG9vbHMgaW4gdGhlIHBsYW5lYSBzZXQKYGBge3Isd2FybmluZz1GQUxTRX0KdW5pcXVlKHBsYW5lYSRjbGF2ZSkgJT4lIGxlbmd0aCgpCmBgYAoKYGBge3J9CnNjaG9vbHNfbXVuIDwtIHNjaG9vbHMgJT4lIGdyb3VwX2J5KENMQVZFKSAlPiUgc3VtbWFyaXNlKG51bT1uKCkpCmFyZWFzX2RmICU8PiUgbGVmdF9qb2luKHNjaG9vbHNfbXVuLGJ5PWMoIkNWRUdFTyI9IkNMQVZFIikpCmFyZWFzX2RmICU8PiUgbXV0YXRlKGRlbnNpdHkgPSBudW0vYXJlYSkKYGBgCgoKVGhlIGRlbnNpdHkgaXMgbG9nLW5vcm1hbApBcyB3ZWxsIGFzIHRoZSB0ZXN0IHJlc3VsdHMuIFNob3VsZCBjb25zaWRlciB0aGlzIHdoZW4gZG9pbmcgcmVncmVzc2lvbnMuCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmdncGxvdChhcmVhc19kZikrCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGxvZzEwKGRlbnNpdHkpKSkrCiAgdGgKYGBgCgpgYGB7cix3YXJuaW5nPUZBTFNFfQpnZW9tX211bl9zY2hvb2wgPC0gZ2VvbV9tdW5pICU+JSBsZWZ0X2pvaW4oYXJlYXNfZGYsIGJ5ID0gYygiQ1ZFR0VPIikpCmBgYAoKYGBge3Isd2FybmluZz1GQUxTRX0KbGlicmFyeShsYXRleDJleHApCmdncGxvdCgpKwogIGdlb21fcG9seWdvbihkYXRhID0gZ2VvbV9tdW5fc2Nob29sLAogICAgICAgICAgICAgICBhZXMobG9uZyxsYXQsbGFiZWw9Q1ZFR0VPLGdyb3VwPWdyb3VwLAogICAgICAgICAgICAgICAgICAgZmlsbD0oZGVuc2l0eSkpLGNvbG9yPSJncmV5IixzaXplPTAuMDEpKwogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gIm1lcmNhdG9yIikrCiAgIyBzY2FsZV9maWxsX2NvbnRpbnVvdXMobmFtZT0iUGVyY2VudGFnZSIsbG93PSJ3aGl0ZSIsIGhpZ2g9IiM0YzEyNjIiLCBndWlkZT0iY29sb3JiYXIiKSsjLGxpbWl0cz1jKDAsMTAwKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudCh0cmFucz0ibG9nMTAiLG5hbWU9IkRlbnNpdHkiLGxvdz0id2hpdGUiLCBoaWdoPSIjNGMxMjYyIiwgZ3VpZGU9ImNvbG9yYmFyIixsYWJlbHM9YygwLDAuNSwxLDEuNSwyLDIuNSkpKyMsbGltaXRzPWMoMCwxMDApKSsKICBsYWJzKHRpdGxlPVRlWCgiU2Nob29scyBieSAkS21eMiQiKSkrCiAgdGhlbWUoI3BhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5IiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JheTIwIiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdD0wKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIixoanVzdD0wLjM5KSkKCmdnc2F2ZSgiLi4vLi4vZmlncy9tYXBfc2Nob29sX2RlbnNpdHkucG5nIixoZWlnaHQgPSAzMCwgd2lkdGggPSAzMCwgdW5pdHMgPSAiY20iKQpgYGAKCgpJIHRoaW5rIEkgaGF2ZW4ndCB3cml0dGVuIHRoZSBpbmdlc3Rpb24gY29kZSBmb3IgdGhlIHNjaG9vbHMgaW50byB0aGUgc3FsIGRhdGFiYXNlLiAqVE9ETyoKQWx0aG91Z2ggSSdtIG5vdCB1c2luZyBpdC4KCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmNvbmFmZSA8LSByZWFkX2RlbGltKCIvaG9tZS9vbGxpbi9Eb2N1bWVudHMvVVZNL2NvdXJzZXMvRFMyL3Byb2plY3QvcGxhbmVhL2RhdGEvY2xlYW4vdHJfY29uYWZlLmNzdiIsZGVsaW09InwiKQpjb25hZmUgJTw+JSBtdXRhdGUoQ1ZFR0VPPXBhc3RlMChFTlQsTVVOKSkKY29uYWZlICU+JSBncm91cF9ieShDVkVHRU8pICU+JSBzdW1tYXJpc2UobnVtID0gbigpKQpgYGAKCgoKIyBNYXBzIGFyZSBjb29sIGJ1dCB3ZSBuZWVkIG90aGVyIHN0dWZmLCBsZXQncyBtYWtlIHJpZGdlcyBmb3IgYWNoaWV2ZW1lbnQKCmBgYHtyfQpsaWJyYXJ5KGdncmlkZ2VzKQpgYGAKCmBgYHtyfQptYXJfdGlkeSA8LSBtYXJfc2NvcmVzICU+JSBwaXZvdF9sb25nZXIoYyhJX2xlbixJSV9sZW4sSUlJX2xlbixJVl9sZW4sSV9tYXQsSUlfbWF0LElJSV9tYXQsSVZfbWF0KSkgJT4lIHVuaXF1ZSgpCmBgYAoKIyBGaXJzdCBmb3IgYWxsCgpgYGB7cn0KZ2dwbG90KG1hcl90aWR5W2dyZXAoImxlbiIsbWFyX3RpZHkkbmFtZSksXSwgYWVzKHggPSB2YWx1ZSwgeSA9IG5hbWUsIGhlaWdodCA9IHN0YXQoZGVuc2l0eSkpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzdGF0ID0gImRlbnNpdHkiKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZT0iUGVyY2VudGFnZSIpKwogIHNjYWxlX3lfZGlzY3JldGUobmFtZT0iTGV2ZWwiKSsKICB0aApnZ3NhdmUoIi4uLy4uL2ZpZ3MvcmlkZ2VzX2xlbi5wbmciLGhlaWdodCA9IDMwLCB3aWR0aCA9IDMwLCB1bml0cyA9ICJjbSIpCmBgYAoKYGBge3J9CmdncGxvdChtYXJfdGlkeVtncmVwKCJtYXQiLG1hcl90aWR5JG5hbWUpLF0sIGFlcyh4ID0gdmFsdWUsIHkgPSBuYW1lLCBoZWlnaHQgPSBzdGF0KGRlbnNpdHkpKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdCA9ICJkZW5zaXR5IikrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWU9IlBlcmNlbnRhZ2UiKSsKICBzY2FsZV95X2Rpc2NyZXRlKG5hbWU9IkxldmVsIikrCiAgdGgKZ2dzYXZlKCIuLi8uLi9maWdzL3JpZGdlc19tYXRoX2xvZy5wbmciLGhlaWdodCA9IDMwLCB3aWR0aCA9IDMwLCB1bml0cyA9ICJjbSIpCmBgYAoKV2UgY29uZHVjdGVkIGFuIEFuZGVyc29uLURhcmxpbmcgdGVzdCB0byBhc3Nlc3MgdGhlIG5vcm1hbGl0eSBvZiBkaXN0cmlidXRpb25zIGFsb25nIGFjaGlldmVtZW50IGxldmVscwoKYGBge3IsZWNobz1GQUxTRSx3YXJuaW5nPUZBTFNFLGluY2x1ZGU9RkFMU0V9CiMgcGFxdWV0aW5lcyA8LSBjKCJzdHJpbmdpIiwicmVzaGFwZTIiLCJwbHlyIiwiZHBseXIiLCJ0aWR5ciIsInNldHMiLCJwbG90bHkiLCJyZWFkciIsIm5vcnRlc3QiLAojICAgICAgICAgICAgICAgICAiZ2dwbG90MiIsImx1YnJpZGF0ZSIsImUxMDcxIiwidXNlZnVsIiwibWFncml0dHIiLCJnb3dlciIsImNsdXN0ZXIiLCJlcXVpdmFsZW5jZSIsCiMgICAgICAgICAgICAgICAgICJmYWN0b2V4dHJhIiwiTmJDbHVzdCIsInJlYWRyIiwiRGVzY1Rvb2xzIiwiZ3JpZEV4dHJhIiwiZWdnIikKIyBub19pbnN0YWxhZG9zIDwtIHBhcXVldGluZXNbIShwYXF1ZXRpbmVzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCJQYWNrYWdlIl0pXQojIGlmKGxlbmd0aChub19pbnN0YWxhZG9zKSkgaW5zdGFsbC5wYWNrYWdlcyhub19pbnN0YWxhZG9zKQojIGxhcHBseShwYXF1ZXRpbmVzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCmBgYAoKY29tcHV0ZSBob3cgbGlrZWx5IGFyZSB0aGVtIHRvIGJlIGRyYXduZWQgYnkgYSBub3JtYWwgZGlzdHJpYnV0aW9uCmBgYHtyLGVjaG89RkFMU0Usd2FybmluZz1GQUxTRSxpbmNsdWRlPVR9CkRhdGEgPC0gbWFyX3Njb3JlcyAlPiUgZHBseXI6OnNlbGVjdChJX21hdCxJSV9tYXQsSUlJX21hdCxJVl9tYXQpCkRhdGEgJTw+JSBsb2cxMCgpCkRhdGEgPC0gRGF0YVthcHBseShEYXRhLCAxLCBmdW5jdGlvbihyb3cpIGFsbChpcy5maW5pdGUocm93KSkpLF0KRGF0YXMgPC0gRGF0YSAlPiUgc2FtcGxlX24oMTUwMCxyZXBsYWNlID0gRikKc2FwcGx5KERhdGFzLCBmdW5jdGlvbih4KSBzaGFwaXJvLnRlc3QoYXMubnVtZXJpYyh4KSkgKQpgYGAKVGhpcyBtZWFucyB0aGF0IG91ciB2YWx1ZXMgYXJlIG5vdCBub3JtYWwKCgpjb21wdXRlIGhvdyBsaWtlbHkgYXJlIHRoZW0gdG8gYmUgZHJhd25lZCBieSBhIHRoZSBzYW1lIGRpc3RyaWJ1dGlvbgoKCmBgYHtyfQpjb21ib3MgPC0gY29tYm4oNCwyKQpwbHlyOjphZHBseShjb21ib3MsIDIsIGZ1bmN0aW9uKHgpIHsKICB0ZXN0IDwtIHQudGVzdChEYXRhc1ssIGFzLm51bWVyaWMoeFsxXSldLCBEYXRhWywgYXMubnVtZXJpYyh4WzJdKV0sYWx0ZXJuYXRpdmUgPSAidCIpCgogIG91dCA8LSBkYXRhLmZyYW1lKCJ2YXIxIiA9IGNvbG5hbWVzKERhdGEpW3hbMV1dCiAgICAgICAgICAgICAgICAgICAgLCAidmFyMiIgPSBjb2xuYW1lcyhEYXRhW3hbMl1dKQogICAgICAgICAgICAgICAgICAgICwgInQudmFsdWUiID0gc3ByaW50ZigiJS41ZiIsIHRlc3Qkc3RhdGlzdGljKQogICAgICAgICAgICAgICAgICAgICwgICJkZiI9IHRlc3QkcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgLCAgInAudmFsdWUiID0gdGVzdCRwLnZhbHVlI3NwcmludGYoIiUuNWYiLCB0ZXN0JHAudmFsdWUpCiAgICAgICAgICAgICAgICAgICAgKQogIHJldHVybihvdXQpCgp9KQojIG1hcl9zY29yZXMgJT4lCiMgICBkcGx5cjo6c2VsZWN0KElfbGVuLElJX2xlbixJSUlfbGVuLElWX2xlbikgJT4lCiMgICBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JQojICAgcHVycnI6Om1hcF9kZih+IGJyb29tOjp0aWR5KHQudGVzdCguIH4gZ3JwKSksIC5pZCA9ICd2YXInKQpgYGAKCgoKCgpgYGB7cn0KZ2dwbG90KG1hcl90aWR5W2dyZXAoImxlbiIsbWFyX3RpZHkkbmFtZSksXSwgYWVzKHggPSB2YWx1ZSwgeSA9IG5hbWUsIGhlaWdodCA9IHN0YXQoZGVuc2l0eSkpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzdGF0ID0gImRlbnNpdHkiKSsKICBzY2FsZV94X2xvZzEwKG5hbWU9IlBlcmNlbnRhZ2UiKSsKICBzY2FsZV95X2Rpc2NyZXRlKG5hbWU9IkxldmVsIikrCiAgdGgKCmdnc2F2ZSgiLi4vLi4vZmlncy9yaWRnZXNfbGFuZ3VhZ2VfbG9nLnBuZyIsaGVpZ2h0ID0gMzAsIHdpZHRoID0gMzAsIHVuaXRzID0gImNtIikKYGBgCgpjb21wdXRlIGhvdyBsaWtlbHkgYXJlIHRoZW0gdG8gYmUgaGF2ZSB0aGUgc2FtZSBtZWFuCmBgYHtyLGVjaG89RkFMU0Usd2FybmluZz1GQUxTRSxpbmNsdWRlPVR9CkRhdGEgPC0gbWFyX3Njb3JlcyAlPiUgZHBseXI6OnNlbGVjdChJX21hdCxJSV9tYXQsSUlJX21hdCxJVl9tYXQpCkRhdGEgJTw+JSBsb2cxMCgpCkRhdGEgPC0gRGF0YVthcHBseShEYXRhLCAxLCBmdW5jdGlvbihyb3cpIGFsbChpcy5maW5pdGUocm93KSkpLF0KRGF0YXMgPC0gRGF0YSAlPiUgc2FtcGxlX24oMTUwMCxyZXBsYWNlID0gRikKc2FwcGx5KERhdGFzLCBmdW5jdGlvbih4KSBzaGFwaXJvLnRlc3QoYXMubnVtZXJpYyh4KSkgKQpgYGAKCmNvbXB1dGUgaG93IGxpa2VseSBhcmUgdGhlbSB0byBoYXZlIHRoZSBzYW1lIG1lYW4KCgpgYGB7cn0KY29tYm9zIDwtIGNvbWJuKDQsMikKcGx5cjo6YWRwbHkoY29tYm9zLCAyLCBmdW5jdGlvbih4KSB7CiAgdGVzdCA8LSB0LnRlc3QoRGF0YVssIGFzLm51bWVyaWMoeFsxXSldLCBEYXRhWywgYXMubnVtZXJpYyh4WzJdKV0sYWx0ZXJuYXRpdmUgPSAidCIpCgogIG91dCA8LSBkYXRhLmZyYW1lKCJ2YXIxIiA9IGNvbG5hbWVzKERhdGEpW3hbMV1dCiAgICAgICAgICAgICAgICAgICAgLCAidmFyMiIgPSBjb2xuYW1lcyhEYXRhW3hbMl1dKQogICAgICAgICAgICAgICAgICAgICwgInQudmFsdWUiID0gc3ByaW50ZigiJS41ZiIsIHRlc3Qkc3RhdGlzdGljKQogICAgICAgICAgICAgICAgICAgICwgICJkZiI9IHRlc3QkcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgLCAgInAudmFsdWUiID0gdGVzdCRwLnZhbHVlI3NwcmludGYoIiUuNWYiLCB0ZXN0JHAudmFsdWUpCiAgICAgICAgICAgICAgICAgICAgKQogIHJldHVybihvdXQpCgp9KQojIG1hcl9zY29yZXMgJT4lCiMgICBkcGx5cjo6c2VsZWN0KElfbGVuLElJX2xlbixJSUlfbGVuLElWX2xlbikgJT4lCiMgICBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JQojICAgcHVycnI6Om1hcF9kZih+IGJyb29tOjp0aWR5KHQudGVzdCguIH4gZ3JwKSksIC5pZCA9ICd2YXInKQpgYGAKCgoKCgoKCgpUaGVuIHRoZSBmYWNldHMgYnkgbWFyZ2luYWxpemF0aW9uIGxldmVsCgoKCmBgYHtyfQpnZ3Bsb3QobWFyX3RpZHlbZ3JlcCgibWF0IixtYXJfdGlkeSRuYW1lKSxdLCBhZXMoeCA9IHZhbHVlLCB5ID0gbmFtZSxmaWxsPWdtLGFscGhhPTAuNiwgaGVpZ2h0ID0gc3RhdChkZW5zaXR5KSkpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdCA9ICJkZW5zaXR5IikrCiAgc2NhbGVfeF9sb2cxMCgpKwogIHRoKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogIGZhY2V0X2dyaWQofmdtKSsKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKYGBgCgoKCgpgYGB7cn0KZ2dwbG90KG1hcl90aWR5W2dyZXAoImxlbiIsbWFyX3RpZHkkbmFtZSksXSwgYWVzKHggPSB2YWx1ZSwgeSA9IGdtLGZpbGw9bmFtZSxhbHBoYT0wLjYsIGhlaWdodCA9IHN0YXQoZGVuc2l0eSkpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJNYXJnaW5hbGl6YXRpb24iLCB2YWx1ZXMgPSBjKCJIaWdoIiA9IGFsdG9jLCAiTG93IiA9IGJham9jLCAiTWVkaXVtIiA9IG1lZGlvYywgIlZlcnkgSGlnaCIgPSBtYWx0b2MsICJWZXJ5IExvdyIgPSBtYmFqb2MpLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IExvdyIsICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpKwogICAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzdGF0ID0gImRlbnNpdHkiKSsKICBzY2FsZV94X2xvZzEwKCkrCiAgICB0aCsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogICAgZmFjZXRfZ3JpZCh+bmFtZSkrCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKYGBgCgoKYGBge3J9CmdncGxvdChtYXJfdGlkeVshZ3JlcGwoIklJXyIsbWFyX3RpZHkkbmFtZSkgJiBncmVwbCgiSV8iLG1hcl90aWR5JG5hbWUpLF0sIGFlcyh4ID0gdmFsdWUsIHkgPSBuYW1lLGZpbGw9Z20sYWxwaGE9MC42LCBoZWlnaHQgPSBzdGF0KGRlbnNpdHkpKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiTWFyZ2luYWxpemF0aW9uIiwgdmFsdWVzID0gYygiSGlnaCIgPSBhbHRvYywgIkxvdyIgPSBiYWpvYywgIk1lZGl1bSIgPSBtZWRpb2MsICJWZXJ5IEhpZ2giID0gbWFsdG9jLCAiVmVyeSBMb3ciID0gbWJham9jKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiVmVyeSBMb3ciLCAiTG93IiwgIk1lZGl1bSIsICJIaWdoIiwgIlZlcnkgSGlnaCIpKSsKICAgIGdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdCA9ICJkZW5zaXR5IikrCiAgc2NhbGVfeF9sb2cxMChuYW1lPSJQZXJjZW50YWdlIikrCiAgc2NhbGVfeV9kaXNjcmV0ZShuYW1lPSIgIikrCiAgICB0aCsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogICAgZmFjZXRfZ3JpZCh+Z20pKwogICAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSIjYjUzYTMxIikpCgpnZ3NhdmUoIi4uLy4uL2ZpZ3MvcmlkZ2VzX21hcmdfSS5wbmciLGhlaWdodCA9IDE1LCB3aWR0aCA9IDMwLCB1bml0cyA9ICJjbSIpCmBgYAoKV2UgdGVzdCB0aGUgaG9tb2dlbmVpdHkgb2YgdmFyaWFuY2VzIHdpdGggYW4gRi10ZXN0IGJlY2F1c2Ugd2Ugd2FudCB0byBzZWUgaG93IHdlbGwgZG9lcyBhbiBhY2hpZXZlbWVudCBsZXZlbCBpbiBvbmUgdGVzdCBleHBsYWlucyB0aGUgb3RoZXIuCgpgYGB7cn0KdG9jb21wIDwtIG1hcl9zY29yZXMgJT4lCiAgZ3JvdXBfYnkoZ20pICU+JQogIHNlbGVjdChnbSxJX2xlbixJX21hdCxJVl9sZW4sSVZfbWF0KQpsb2dzcyA8LSBzYXBwbHkodG9jb21wICU+JSB1bmdyb3VwKGdtKSAlPiUgc2VsZWN0KC1nbSksIGZ1bmN0aW9uKHgpIGxvZzEwKGFzLm51bWVyaWMoeCkpICkKdG9jb21wWzI6NV0gPC0gbG9nc3MKdG9jb21wIDwtIHRvY29tcFthcHBseSh0b2NvbXAgJT4lIHVuZ3JvdXAoZ20pICU+JSBzZWxlY3QoLWdtKSwgMSwgZnVuY3Rpb24ocm93KSBhbGwoaXMuZmluaXRlKHJvdykpKSxdCiMgdG9jb21wIDwtIHRvY29tcCAlPiUgc2FtcGxlX24oMjUwLHJlcGxhY2UgPSBGKQoKdG9jb21wICU+JQogIGdyb3VwX2J5KGdtKSAlPiUKICBzdW1tYXJpc2UocHZhbCA9IHZhci50ZXN0KElfbGVuLElfbWF0LGFsdGVybmF0aXZlPSJ0IikkcC52YWx1ZSkKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QobWFyX3RpZHlbZ3JlcGwoIklWXyIsbWFyX3RpZHkkbmFtZSksXSwgYWVzKHggPSAodmFsdWUpLCB5ID0gbmFtZSxmaWxsPWdtLGFscGhhPTAuNiwgaGVpZ2h0ID0gc3RhdChkZW5zaXR5KSkpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzKHN0YXQgPSAiZGVuc2l0eSIpKwogIHNjYWxlX3hfbG9nMTAobmFtZT0iUGVyY2VudGFnZSIpKwogIHNjYWxlX3lfZGlzY3JldGUobmFtZT0iICIpKwogICAgdGgrCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICAgIGZhY2V0X2dyaWQofmdtKSsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQpnZ3NhdmUoIi4uLy4uL2ZpZ3MvcmlkZ2VzX21hcmdfSVYucG5nIixoZWlnaHQgPSAxNSwgd2lkdGggPSAzMCwgdW5pdHMgPSAiY20iKQpgYGAKCgpVc2UgdGhlIEZsaWduZXItS2lsbGVuIG1lZGlhbiB0ZXN0IHRvIGNvbXBhcmUgdGhlIGhvbW9nZW5laXR5IG9mIHZhcmlhbmNlcwoKYGBge3J9CnRvY29tcCA8LSBtYXJfc2NvcmVzICU+JQogIGdyb3VwX2J5KGdtKSAlPiUKICBzZWxlY3QoZ20sSV9sZW4sSV9tYXQsSVZfbGVuLElWX21hdCkKbG9nc3MgPC0gc2FwcGx5KHRvY29tcCAlPiUgdW5ncm91cChnbSkgJT4lIHNlbGVjdCgtZ20pLCBmdW5jdGlvbih4KSBsb2cxMChhcy5udW1lcmljKHgpKSApCnRvY29tcFsyOjVdIDwtIGxvZ3NzCnRvY29tcCA8LSB0b2NvbXBbYXBwbHkodG9jb21wICU+JSB1bmdyb3VwKGdtKSAlPiUgc2VsZWN0KC1nbSksIDEsIGZ1bmN0aW9uKHJvdykgYWxsKGlzLmZpbml0ZShyb3cpKSksXQojIHRvY29tcCA8LSB0b2NvbXAgJT4lIHNhbXBsZV9uKDMwMCxyZXBsYWNlID0gRikKCnRvY29tcCAlPiUKICBncm91cF9ieShnbSkgJT4lCiAgc3VtbWFyaXNlKHB2YWwgPSB2YXIudGVzdChJVl9sZW4sSVZfbWF0LGFsdGVybmF0aXZlPSJ0IikkcC52YWx1ZSkKYGBgCgpgYGB7cn0KdG9jb21wIDwtIHNjaG9vbHNbIWlzLm5hKHNjaG9vbHMkZ20pLF0gJT4lCiAgZ3JvdXBfYnkoZ20pICU+JQogIHNlbGVjdChnbSxJX3BvcmNfbGVuLElfcG9yY19tYXQsSVZfcG9yY19sZW4sSVZfcG9yY19tYXQpCmxvZ3NzIDwtIHNhcHBseSh0b2NvbXAgJT4lIHVuZ3JvdXAoZ20pICU+JSBzZWxlY3QoLWdtKSwgZnVuY3Rpb24oeCkgbG9nMTAoYXMubnVtZXJpYyh4KSkgKQp0b2NvbXBbMjo1XSA8LSBsb2dzcwp0b2NvbXAgPC0gdG9jb21wW2FwcGx5KHRvY29tcCAlPiUgdW5ncm91cChnbSkgJT4lIHNlbGVjdCgtZ20pLCAxLCBmdW5jdGlvbihyb3cpIGFsbChpcy5maW5pdGUocm93KSkpLF0KIyB0b2NvbXAgPC0gdG9jb21wICU+JSBzYW1wbGVfbigyNTAscmVwbGFjZSA9IEYpCgp0b2NvbXAgJT4lCiAgZ3JvdXBfYnkoZ20pICU+JQogIHN1bW1hcmlzZShwdmFsID0gdmFyLnRlc3QoSVZfcG9yY19sZW4sSVZfcG9yY19tYXQsYWx0ZXJuYXRpdmU9InQiKSRwLnZhbHVlKQpgYGAKCgoKIyMgQnV0IHdoYXQgYWJvdXQgc2Nob29sIHR5cGUKCmBgYHtyfQptYXJfdGlkeSA8LSBtYXJfc2NvcmVzICU+JSBwaXZvdF9sb25nZXIoYyhJX2xlbixJSV9sZW4sSUlJX2xlbixJVl9sZW4sSV9tYXQsSUlfbWF0LElJSV9tYXQsSVZfbWF0KSkgJT4lIHVuaXF1ZSgpCmBgYAoKYGBge3J9Cm1hcl9zY29yZXMgJTw+JSBsZWZ0X2pvaW4oc2Nob29scyAlPiUgc2VsZWN0KENMQVZFLHRpcG9fZXNjdWVsYSksYnk9IkNMQVZFIikgJT4lIHVuaXF1ZSgpCm1hcl90aWR5MiA8LSBtYXJfc2NvcmVzICU+JSBwaXZvdF9sb25nZXIoYyhJX2xlbixJSV9sZW4sSUlJX2xlbixJVl9sZW4sSV9tYXQsSUlfbWF0LElJSV9tYXQsSVZfbWF0KSkgJT4lIHVuaXF1ZSgpCmdncGxvdChtYXJfdGlkeTJbZ3JlcGwoIklfIixtYXJfdGlkeTIkbmFtZSksXSwgYWVzKHggPSAodmFsdWUpLCB5ID0gbmFtZSxmaWxsPXRpcG9fZXNjdWVsYSxhbHBoYT0wLjYsIGhlaWdodCA9IHN0YXQoZGVuc2l0eSkpKSArCiAgIyBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgIyAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiVmVyeSBMb3ciLCAiTG93IiwgIk1lZGl1bSIsICJIaWdoIiwgIlZlcnkgSGlnaCIpKSsKICAgIGdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdCA9ICJkZW5zaXR5IikrCiAgc2NhbGVfeF9sb2cxMCgpKwogICAgdGgrCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICAgIGZhY2V0X2dyaWQofnRpcG9fZXNjdWVsYSkrCiAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IiNiNTNhMzEiKSkKYGBgCgojIEJ1aWxkIHRoZSBkaXN0cmlidXRpb24gZGlmZmVyZW5jZSB2YXJpYWJsZQoKYGBge3J9CnNjaG9vbHMkSV9wb3JjX21hdCAtIHNjaG9vbHMkSV9wb3JjX2xlbgpzY2hvb2xzICU8PiUgbXV0YXRlKGRpZmZJID0gSV9wb3JjX21hdCAtIElfcG9yY19sZW4sIGRpZmZJViA9IElWX3BvcmNfbWF0IC0gSVZfcG9yY19sZW4pCnNjaF90aWR5IDwtIHNjaG9vbHMgJT4lIHBpdm90X2xvbmdlcihjKGRpZmZJLCBkaWZmSVYpKSAlPiUgdW5pcXVlKCkKYGBgCgoKYGBge3J9CmdncGxvdChzY2hfdGlkeSwgYWVzKHggPSAodmFsdWUpLCB5ID0gbmFtZSxmaWxsPWdtLGFscGhhPTAuNiwgaGVpZ2h0ID0gc3RhdChkZW5zaXR5KSkpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIk1hcmdpbmFsaXphdGlvbiIsIHZhbHVlcyA9IGMoIkhpZ2giID0gYWx0b2MsICJMb3ciID0gYmFqb2MsICJNZWRpdW0iID0gbWVkaW9jLCAiVmVyeSBIaWdoIiA9IG1hbHRvYywgIlZlcnkgTG93IiA9IG1iYWpvYyksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlZlcnkgTG93IiwgIkxvdyIsICJNZWRpdW0iLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkrCiAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzKHN0YXQgPSAiZGVuc2l0eSIpKwogIHNjYWxlX3hfbG9nMTAobmFtZT0iUGVyY2VudGFnZSIpKwogIHNjYWxlX3lfZGlzY3JldGUobmFtZT0iICIpKwogICAgdGgrCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSwKICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICAgIGZhY2V0X2dyaWQofmdtKSsKICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2I1M2EzMSIpKQoKZ2dwbG90KHNjaG9vbHNbY29tcGxldGUuY2FzZXMoc2Nob29scyRnbSksXSxhZXMoZGlmZklWLGZpbGw9Z20pKSsKICBnZW9tX2RlbnNpdHkoKQoKZ2dwbG90KHNjaG9vbHNbIWlzLm5hKHNjaG9vbHMkZ20pLF0sYWVzKHg9KGRpZmZJVisxMDApLzIwMCx5PWdtLGZpbGw9Z20saGVpZ2h0ID0gc3RhdChkZW5zaXR5KSkpKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoc3RhdD0iZGVuc2l0eSIpKwogIHNjYWxlX3hfbG9nMTAoKQoKCiMgZ2dzYXZlKCIuLi8uLi9maWdzL3JpZGdlc19tYXJnX0lWLnBuZyIsaGVpZ2h0ID0gMTUsIHdpZHRoID0gMzAsIHVuaXRzID0gImNtIikKYGBgCgpgYGB7cn0KZ2dwbG90KHNjaG9vbHNbIWlzLm5hKHNjaG9vbHMkZ20pLF0sYWVzKHg9KGRpZmZJKzEwMCkvMjAwLHk9Z20sZmlsbD1nbSxoZWlnaHQgPSBzdGF0KGRlbnNpdHkpKSkrCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzdGF0PSJkZW5zaXR5IikrCiAgc2NhbGVfeF9sb2cxMCgpI2xpbWl0cyA9IGMoLTUwLDUwKSkKYGBgCgptYXRlIC0gbGVuZ3VhCmBgYHtyfQpjaV9tZWRpYW4gPC0gZnVuY3Rpb24oeCl7CiAgYm9vdG1lZCA9IGFwcGx5KG1hdHJpeChzYW1wbGUoeCwgcmVwPVRSVUUsIDEwXjQqbGVuZ3RoKHgpKSwgbnJvdz0xMF4zKSwgMSwgbWVkaWFuKQogIHF1YW50aWxlKGJvb3RtZWQsIGMoLjAyNSwwLjUsIDAuOTc1KSkKfQoKc2Nob29sc1tjb21wbGV0ZS5jYXNlcyhzY2hvb2xzJGdtKSxdICU+JSBncm91cF9ieShnbSkgJT4lIHN1bW1hcmlzZShtZWRpYW4oZGlmZkkpLG1lZGlhbihkaWZmSVYpKQpzY2hvb2xzICU+JSBncm91cF9ieShnbSkgJT4lIHN1bW1hcmlzZShjaV9tZWRpYW4obG9nMTAoKGRpZmZJVisxMDApLzIwMCkpWzJdKQpgYGAKCgpgYGB7cn0Kc2Nob29sc1tjb21wbGV0ZS5jYXNlcyhzY2hvb2xzJGdtKSxdICU+JSBncm91cF9ieShnbSkgJT4lIHN1bW1hcmlzZShtZWRpYW4oZGlmZkkpLGNpX21lZGlhbihkaWZmSVYpWzFdLGNpX21lZGlhbihkaWZmSVYpWzNdKQpgYGAKCgoKIyBMZXQncyBjaGVjayB0aGUgQmF5ZXNpYW4gbmV0d29ya3MKIyBQdWVydG8gTW9yZWxvcyBiZWNhbWUgbXVuaWNpcGFsaXR5IGluIDIwMTEKCmBgYHtyfQplbmNvZGVfb3JkaW5hbCA8LSBmdW5jdGlvbih4LCBvcmRlciA9IHVuaXF1ZSh4KSkgewogIHggPC0gYXMubnVtZXJpYyhmYWN0b3IoeCwgbGV2ZWxzID0gb3JkZXIsIGV4Y2x1ZGUgPSBOVUxMKSkKICB4Cn0KYGBgCgoKYGBge3J9CnNjaG9vbHMgPC0gc2Nob29scyAlPiUgc2VsZWN0KC1vdnNkLC1vdnNkc2UsLWluZDBhMTAwKQpzY2hvb2xzIDwtIHNjaG9vbHNbY29tcGxldGUuY2FzZXMoc2Nob29scyksXQphcmVhc19kZiA8LSBhcmVhc19kZiAlPiUgbXV0YXRlKENMQVZFPUNWRUdFTykgJT4lIHNlbGVjdCgtQ1ZFR0VPKQppbmRpZwpgYGAKCgpgYGB7cn0KZm9yX2JuIDwtIHNjaG9vbHMgJT4lIGxlZnRfam9pbihpbmRpZyAlPiUgc2VsZWN0KENMQVZFLElQT0JfSU5ESSxJUGhsaSkpCmZvcl9ibiA8LSBmb3JfYm4gJT4lIG11dGF0ZShwX2luZGlnID0gSVBPQl9JTkRJL3BvcF9tYXJnLGxhbmdfaW5kaWc9SVBobGkvSVBPQl9JTkRJKQpmb3JfYm4gJTw+JSBsZWZ0X2pvaW4oYXJlYXNfZGYsYnk9IkNMQVZFIikKCmZvcl9iblshY29tcGxldGUuY2FzZXMoZm9yX2JuKSxdJElQT0JfSU5ESSA8LSAwCmZvcl9iblshY29tcGxldGUuY2FzZXMoZm9yX2JuKSxdJHBfaW5kaWcgPC0gMApkYXRhYm4gPC0gZm9yX2JuICU+JSB1bmdyb3VwKENMQVZFLE5PTV9FTlQpICU+JQogIHNlbGVjdChkaWZmSSxkaWZmSVYsdHVybm8sZ20sdGlwb19lc2N1ZWxhLGlsbGl0ZXJhY3ksZWxlbWVudGFyeSxub19zZXdhZ2Usbm9fZWxlY3RyaWNpdHksbm9fd2F0ZXIsb3ZlcmNyb3dkaW5nLGRpcnRfZmxvb3IsbGVzc181ayxsZXNzXzJtaW53YWdlLGltLGx1Z2FyLGx1Z2FyZXN0LElQT0JfSU5ESSxwX2luZGlnLGFyZWEsbnVtLGRlbnNpdHksbGFuZ19pbmRpZykKICAjIHNlbGVjdChJX3BvcmNfbGVuLElJX3BvcmNfbGVuLElJSV9wb3JjX2xlbixJVl9wb3JjX2xlbixJX3BvcmNfbWF0LElJX3BvcmNfbWF0LElJSV9wb3JjX21hdCxJVl9wb3JjX21hdCx0dXJubyxnbSx0aXBvX2VzY3VlbGEsaWxsaXRlcmFjeSxlbGVtZW50YXJ5LG5vX3Nld2FnZSxub19lbGVjdHJpY2l0eSxub193YXRlcixvdmVyY3Jvd2RpbmcsZGlydF9mbG9vcixsZXNzXzVrLGxlc3NfMm1pbndhZ2UsaW0sbHVnYXIsbHVnYXJlc3QsSVBPQl9JTkRJLHBfaW5kaWcsYXJlYSxudW0sZGVuc2l0eSxsYW5nX2luZGlnKQoKZGF0YWJuCgojIEkgc2hvdWxkIGF1dG9tYXRlIHRoaXMKZGF0YWJuJHR1cm5vIDwtIGVuY29kZV9vcmRpbmFsKGRhdGFibiR0dXJubykKZGF0YWJuJGdtIDwtIGVuY29kZV9vcmRpbmFsKGRhdGFibiRnbSkKZGF0YWJuJHRpcG9fZXNjdWVsYSA8LSBlbmNvZGVfb3JkaW5hbChkYXRhYm4kdGlwb19lc2N1ZWxhKQoKIyBkYXRhYm4kdHVybm8gPC0gYXMuZG91YmxlKGRhdGFibiR0dXJubykKIyBkYXRhYm4kZ20gPC0gYXMuZG91YmxlKGRhdGFibiRnbSkKIyBkYXRhYm4kbnVtIDwtIGFzLmRvdWJsZShkYXRhYm4kbnVtKQojIGRhdGFibiR0aXBvX2VzY3VlbGEgPC0gYXMuZG91YmxlKGRhdGFibiR0aXBvX2VzY3VlbGEpCgpkYXRhYm4yIDwtIGRhdGFibgoKZGF0YWJuJElfcG9yY19sZW4gPC0gbG9nMTAoZGF0YWJuJElfcG9yY19sZW4pCmRhdGFibiRJSV9wb3JjX2xlbiA8LSBsb2cxMChkYXRhYm4kSUlfcG9yY19sZW4pCmRhdGFibiRJSUlfcG9yY19sZW4gPC0gbG9nMTAoZGF0YWJuJElJSV9wb3JjX2xlbikKZGF0YWJuJElWX3BvcmNfbGVuIDwtIGxvZzEwKGRhdGFibiRJVl9wb3JjX2xlbikKCmRhdGFibiRJX3BvcmNfbWF0IDwtIGxvZzEwKGRhdGFibiRJX3BvcmNfbWF0KQpkYXRhYm4kSUlfcG9yY19tYXQgPC0gbG9nMTAoZGF0YWJuJElJX3BvcmNfbWF0KQpkYXRhYm4kSUlJX3BvcmNfbWF0IDwtIGxvZzEwKGRhdGFibiRJSUlfcG9yY19tYXQpCmRhdGFibiRJVl9wb3JjX21hdCA8LSBsb2cxMChkYXRhYm4kSVZfcG9yY19tYXQpCgpkYXRhYm4kZGVuc2l0eSA8LSBsb2cxMChkYXRhYm4kZGVuc2l0eSkKZGF0YWJuJHBfaW5kaWcgPC0gbG9nMTAoZGF0YWJuJHBfaW5kaWcpCmRhdGFibiRsYW5nX2luZGlnIDwtIGxvZzEwKGRhdGFibiRsYW5nX2luZGlnKQoKZGF0YWJuX2Zpbml0ZSA8LSBkYXRhYm5bYXBwbHkoZGF0YWJuLCAxLCBmdW5jdGlvbihyb3cpIGFsbChpcy5maW5pdGUocm93KSkpLF0KZGF0YWJuX2Zpbml0ZSA8LSBkYXRhYm5bYXBwbHkoZGF0YWJuICU+JSBzZWxlY3QoLXR1cm5vLC1nbSwtbnVtLC10aXBvX2VzY3VlbGEpLCAxLCBmdW5jdGlvbihyb3cpIGFsbChpcy5maW5pdGUocm93KSkpLF0KZGF0YWJuX2Zpbml0ZSAlPD4lIHNlbGVjdCgtaW0sLWx1Z2FyLC1sdWdhcmVzdCwtSVBPQl9JTkRJLC1hcmVhLC1udW0pCmBgYAoKCmBgYHtyLGVjaG89RkFMU0Usd2FybmluZz1GQUxTRSxpbmNsdWRlPUZBTFNFfQpwYXF1ZXRpbmVzIDwtIGMoIm1sYmVuY2giLCJjYXJldCIsInBhcnR5IikKbm9faW5zdGFsYWRvcyA8LSBwYXF1ZXRpbmVzWyEocGFxdWV0aW5lcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKV0KaWYobGVuZ3RoKG5vX2luc3RhbGFkb3MpKSBpbnN0YWxsLnBhY2thZ2VzKG5vX2luc3RhbGFkb3MpCmxhcHBseShwYXF1ZXRpbmVzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCmBgYAoKRGVsZXRlIHRoZSBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMKYGBge3J9CnNldC5zZWVkKDQyKQojIGNvcnJNIDwtIGNvcihkYXRhYm5fZmluaXRlWyxjKDM6MTcpXSkKY29yck0gPC0gY29yKGRhdGFibl9maW5pdGUpCmhpZ2hseUNvcnJlbGF0ZWQgPC0gZmluZENvcnJlbGF0aW9uKGNvcnJNLCBjdXRvZmY9MC43MCkKY29sbmFtZXMoZGF0YWJuX2Zpbml0ZVssYygzOjE3KV0pW2hpZ2hseUNvcnJlbGF0ZWRdCmRhdGFibl9maW5pdGUgJTw+JSBzZWxlY3QoLWlsbGl0ZXJhY3ksLWVsZW1lbnRhcnksLW92ZXJjcm93ZGluZywtZGlydF9mbG9vciwtbGVzc181aykKZGF0YWJuX2Zpbml0ZSAlPD4lIHNlbGVjdCgtZ20pCmBgYAoKCgojIFZhcmlhYmxlIHNlbGVjdGlvbgpgYGB7cn0KIyBjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKQojIG1vZGVsIDwtIGNhcmV0Ojp0cmFpbihJX3BvcmNfbGVufi4sIGRhdGE9ZGF0YWJuX2Zpbml0ZSwgbWV0aG9kPSJjZm9yZXN0IiwgcHJlUHJvY2Vzcz0ic2NhbGUiLCB0ckNvbnRyb2w9Y29udHJvbCkKIyAjIGVzdGltYXRlIHZhcmlhYmxlIGltcG9ydGFuY2UKIyBpbXBvcnRhbmNlIDwtIHZhckltcChtb2RlbCwgc2NhbGU9RkFMU0UpCiMgIyBzdW1tYXJpemUgaW1wb3J0YW5jZQojIHByaW50KGltcG9ydGFuY2UpCiMgIyBwbG90IGltcG9ydGFuY2UKIyBwbG90KGltcG9ydGFuY2UpCmBgYAoKCgpgYGB7cn0KIyBzZXQuc2VlZCg0MikKIyBkYXRhYm5fZmluaXRlMiA8LSBhcy5kYXRhLmZyYW1lKGRhdGFibl9maW5pdGUpCiMgI0NvbnRyb2xsaW5nIHRoZSBmZWF0dXJlIHNlbGVjdGlvbiwgdXNpbmcgZGVmYXVsdCBmdW5jdGlvbnMsIHdpdGggMTAgZm9sZHMgY3Jvc3MgdmFsaWRhdGlvbgojIGNvbnRyb2wgPC0gcmZlQ29udHJvbChmdW5jdGlvbnM9cmZGdW5jcywgbWV0aG9kPSJjdiIsIG51bWJlcj0xMCkKIyAjIHJ1biB0aGUgUkZFIGFsZ29yaXRobQojICMgdXNpbmcgcmVjdXJzaXZlIGZlYXR1cmUgZWxpbWluYXRpb24gb3IgYmFja3dhcmRzIHNlbGVjdGlvbgojICMgbGV0J3MgY2hlY2sgZmlyc3QgaWYgaXQgd29ya3MgdGhlIHNhbWUgZm9yIGVhY2ggbGV2ZWwgYW5kIHRlc3QKIyByZXN1bHRzIDwtIHJmZSh4PWRhdGFibl9maW5pdGUyWyxjKDEsMTA6MTkpXSwgeT1kYXRhYm5fZmluaXRlMlssMl0sIHJmZUNvbnRyb2w9Y29udHJvbCkKIyAjIHJlc3VsdHMgPC0gcmZlKGRhdGFiblssYygxLDEwOjI3KV0sIGRhdGFiblssbl0sIHNpemVzPWMoMToxOCksIHJmZUNvbnRyb2w9Y29udHJvbCkKIyAjIHN1bW1hcml6ZSB0aGUgcmVzdWx0cwojIHByaW50KHJlc3VsdHMpCiMgIyBsaXN0IHRoZSBjaG9zZW4gZmVhdHVyZXMKIyBwcmVkaWN0b3JzKHJlc3VsdHMpCiMgIyBwbG90IHRoZSByZXN1bHRzCiMgcGxvdChyZXN1bHRzLCB0eXBlPWMoImciLCAibyIpKQpgYGAKCgpJIGhhdmVuJ3QgZmlndXJlZCBob3cgdG8gc2F2ZSB0aGlzIGdyYXBocyB3aXRoIHRoZSBjaHVuayBvdXRwdXQgaW5saW5lIGluIGEgbm90ZWJvb2ssIHNvIHdlIGhhdmUgdG8gZGlzc2FibGUgaXQgdG8gcnVuIGFuZCBzYXZlIHRoZSBwbG90cwoKYGBge3J9CmZvciAoaSBpbiAxOjcpewogIGJuZiA8LSBibmxlYXJuOjpoMnBjKGRhdGFibl9maW5pdGVbLGMoaSw5OjE3KV0pCiAgcG5nKHBhc3RlMCgiL2ZpZ3MvZ3JhcGhfIixpLCIucG5nIikpCiAgcGxvdChncmFwaHZpei5wbG90KGJuZiwgc2hhcGUgPSAiZWxsaXBzZSIpKQogIGRldi5vZmYoKQogIHN0ci5kaWZmIDwtIGJubGVhcm46OmJvb3Quc3RyZW5ndGgoZGF0YWJuX2Zpbml0ZVssYyhpLDk6MTcpXSxSPTIwLGFsZ29yaXRobT0iaDJwYyIpCiAgYXZnLmRpZmYgPC0gYXZlcmFnZWQubmV0d29yayhzdHIuZGlmZikjLHRocmVzaG9sZCA9IDAuNykKICBwbmcocGFzdGUwKCIvZmlncy9ncmFwaF8iLGksIl9ib290cy5wbmciKSkKICBwbG90KGdyYXBodml6LnBsb3QoYXZnLmRpZmYsIHNoYXBlID0gImVsbGlwc2UiKSkKICBkZXYub2ZmKCkKfQpgYGAKCgoKYGBge3J9CiMgdGhlX2NvbHVtbnMgPC0gYyhkaWZmSSwgdHVybm8sIHRpcG9fZXNjdWVsYSwgaWxsaXRlcmFjeSwgZWxlbWVudGFyeSwgbm9fc2V3YWdlLCBub19lbGVjdHJpY2l0eSwgbm9fd2F0ZXIsIG92ZXJjcm93ZGluZywgZGlydF9mbG9vciwgbGVzc181aywgbGVzc18ybWlud2FnZSwgaW0sIHBfaW5kaWcsIGRlbnNpdHksIGxhbmdfaW5kaWcpCiMgYm5mIDwtIGJubGVhcm46OmgycGMoZGF0YWJuX2Zpbml0ZVssYygxLDM6MTEpXSkKYm5mIDwtIGJubGVhcm46OmgycGMoZGF0YWJuX2Zpbml0ZSAlPiUgc2VsZWN0KGRpZmZJLCB0dXJubywgdGlwb19lc2N1ZWxhLCBpbGxpdGVyYWN5LCBlbGVtZW50YXJ5LCBub19zZXdhZ2UsIG5vX2VsZWN0cmljaXR5LCBub193YXRlciwgb3ZlcmNyb3dkaW5nLCBkaXJ0X2Zsb29yLCBsZXNzXzVrLCBsZXNzXzJtaW53YWdlLCBpbSwgcF9pbmRpZywgZGVuc2l0eSwgbGFuZ19pbmRpZykpCgojIHN0ci5kaWZmIDwtIGJubGVhcm46OmJvb3Quc3RyZW5ndGgoZGF0YWJuX2Zpbml0ZVssYygxLDM6MTEpXSxSPTIwLGFsZ29yaXRobT0iaDJwYyIpCnN0ci5kaWZmIDwtIGJubGVhcm46OmJvb3Quc3RyZW5ndGgoZGF0YWJuX2Zpbml0ZSAlPiUgc2VsZWN0KGRpZmZJLCB0dXJubywgdGlwb19lc2N1ZWxhLCBpbGxpdGVyYWN5LCBlbGVtZW50YXJ5LCBub19zZXdhZ2UsIG5vX2VsZWN0cmljaXR5LCBub193YXRlciwgb3ZlcmNyb3dkaW5nLCBkaXJ0X2Zsb29yLCBsZXNzXzVrLCBsZXNzXzJtaW53YWdlLCBpbSwgcF9pbmRpZywgZGVuc2l0eSwgbGFuZ19pbmRpZyksUj0yMCxhbGdvcml0aG09ImgycGMiKQoKYXZnLmRpZmYgPC0gYXZlcmFnZWQubmV0d29yayhzdHIuZGlmZikjLHRocmVzaG9sZCA9IDAuNykKZ3JhcGh2aXoucGxvdChhdmcuZGlmZiwgc2hhcGUgPSAiZWxsaXBzZSIpCmBgYAoKCmBgYHtyfQpibmYgPC0gYm5sZWFybjo6aDJwYyhkYXRhYm5fZmluaXRlWyxjKDIsMzoxMSldKQogIHN0ci5kaWZmIDwtIGJubGVhcm46OmJvb3Quc3RyZW5ndGgoZGF0YWJuX2Zpbml0ZVssYygyLDM6MTEpXSxSPTIwLGFsZ29yaXRobT0iaDJwYyIpCiAgYXZnLmRpZmYgPC0gYXZlcmFnZWQubmV0d29yayhzdHIuZGlmZikjLHRocmVzaG9sZCA9IDAuNykKICBncmFwaHZpei5wbG90KGF2Zy5kaWZmLCBzaGFwZSA9ICJlbGxpcHNlIikKYGBgCgoKCgoKYGBge3J9CmJuZiA8LSBibmxlYXJuOjpoMnBjKGRhdGFibl9maW5pdGVbLGMoMSw5OjE3KV0pCmZpdHRlZCA8LSBibi5maXQoYm5mLGRhdGFibl9maW5pdGVbLGMoMSw5OjE3KV0pCmBgYAoKCgpgYGB7cn0KZ3JhcGh2aXoucGxvdChibmYsIHNoYXBlID0gImVsbGlwc2UiKQpgYGAKCgpgYGB7cn0KYm5mIDwtIGJubGVhcm46OmgycGMoZGF0YWJuX2Zpbml0ZVssYygxLDM6MTEpXSkKYm5mIDwtIGJubGVhcm46OnJldmVyc2UuYXJjKGJuZixmcm9tPSJ0aXBvX2VzY3VlbGEiLHRvPSJJVl9wb3JjX2xlbiIpCmJuZiA8LSBibmxlYXJuOjpyZXZlcnNlLmFyYyhibmYsZnJvbT0idHVybm8iLHRvPSJJVl9wb3JjX2xlbiIpCmJuZiA8LSBibmxlYXJuOjpyZXZlcnNlLmFyYyhibmYsZnJvbT0iZGVuc2l0eSIsdG89IklWX3BvcmNfbGVuIikKZml0dGVkIDwtIGJuLmZpdChibmYsZGF0YWJuX2Zpbml0ZVssYyg0LDM6MTEpXSkKYGBgCgpgYGB7cn0KZ3JhcGh2aXoucGxvdChibmYsIHNoYXBlID0gImVsbGlwc2UiKQpgYGAKCgpgYGB7cn0KYm5mIDwtIGJubGVhcm46OmgycGMoZGF0YWJuX2Zpbml0ZVssYygyLDM6MTEpXSkKYm5mIDwtIGJubGVhcm46OnJldmVyc2UuYXJjKGJuZixmcm9tPSJ0aXBvX2VzY3VlbGEiLHRvPSJJX3BvcmNfbWF0IikKYm5mIDwtIGJubGVhcm46OnJldmVyc2UuYXJjKGJuZixmcm9tPSJ0dXJubyIsdG89IklfcG9yY19tYXQiKQpibmYgPC0gYm5sZWFybjo6cmV2ZXJzZS5hcmMoYm5mLGZyb209Im92ZXJjcm93ZGluZyIsdG89IklfcG9yY19tYXQiKQpibmYgPC0gYm5sZWFybjo6cmV2ZXJzZS5hcmMoYm5mLGZyb209ImxhbmdfaW5kaWciLHRvPSJJX3BvcmNfbWF0IikKZml0dGVkIDwtIGJuLmZpdChibmYsZGF0YWJuX2Zpbml0ZVssYygyLDM6MTEpXSkKYGBgCgoKYGBge3J9CmdyYXBodml6LnBsb3QoYm5mLCBzaGFwZSA9ICJlbGxpcHNlIikKYGBgCgoKYGBge3J9CmJuZiA8LSBibmxlYXJuOjpoMnBjKGRhdGFibl9maW5pdGVbLGMoOCw5OjE3KV0pCmJuZiA8LSBibmxlYXJuOjpyZXZlcnNlLmFyYyhibmYsZnJvbT0idGlwb19lc2N1ZWxhIix0bz0iSVZfcG9yY19tYXQiKQpibmYgPC0gYm5sZWFybjo6cmV2ZXJzZS5hcmMoYm5mLGZyb209InR1cm5vIix0bz0iSVZfcG9yY19tYXQiKQpmaXR0ZWQgPC0gYm4uZml0KGJuZixkYXRhYm5fZmluaXRlWyxjKDgsOToxNyldKQoKYm4uZml0LnFxcGxvdChmaXR0ZWQpCmJuLmZpdC5iYXJjaGFydChmaXR0ZWQkSVZfcG9yY19tYXQpCmJuLmN2KGRhdGFibl9maW5pdGVbLGMoMSw5OjE3KV0sYm49ImgycGMiKQpibi5jdihkYXRhYm5fZmluaXRlWyxjKDQsOToxNyldLGJuPSJoMnBjIikKCmJuLmN2KGRhdGFibl9maW5pdGVbLGMoNSw5OjE3KV0sYm49ImgycGMiKQphPC1ibi5jdihkYXRhYm5fZmluaXRlWyxjKDgsOToxNyldLGJuPSJoMnBjIikKYGBgCgoKYGBge3J9CmdyYXBodml6LnBsb3QoYm5mLCBzaGFwZSA9ICJlbGxpcHNlIikKCjEgLSBtYXQgbG9nIGxpa2VsaWhvb2QgMTQuNzg1MzkKNCAtIG1hdCBsb2cgbGlrZWxpaG9vZCAxNS4xNzczNQoKMSAtIGxhbiBsb2cgbGlrZWxpaG9vZCAxNC45MzcyNQo0IC0gbGFuIGxvZyBsaWtlbGlob29kIDE0Ljc5MjM4CmBgYAoKCgoKCgoKCgo=